//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX                                                                           XX
XX                             emitArm.cpp                                   XX
XX                                                                           XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

#include "jitpch.h"
#ifdef _MSC_VER
#pragma hdrstop
#endif

#if defined(_TARGET_ARM_)

/*****************************************************************************/
/*****************************************************************************/

#include "instr.h"
#include "emit.h"
#include "codegen.h"

/*****************************************************************************/

const instruction       emitJumpKindInstructions[] =
{
    INS_nop,

    #define JMP_SMALL(en, rev, ins, condcode) INS_##ins,
    #include "emitjmps.h"
};

const emitJumpKind      emitReverseJumpKinds[] =
{
    EJ_NONE,

    #define JMP_SMALL(en, rev, ins, condcode) EJ_##rev,
    #include "emitjmps.h"
};


const unsigned          emitJumpKindCondCodes[] =
{
    15, // illegal

    #define JMP_SMALL(en, rev, ins, condcode) condcode,
    #include "emitjmps.h"
};

/*****************************************************************************
 * Look up the instruction for a jump kind
 */

/*static*/ instruction      emitter::emitJumpKindToIns(emitJumpKind jumpKind)
{
    assert((unsigned)jumpKind < ArrLen(emitJumpKindInstructions));
    return emitJumpKindInstructions[jumpKind];
}

/*****************************************************************************
 * Look up the jump kind for an instruction. It better be a conditional
 * branch instruction with a jump kind!
 */

/*static*/ emitJumpKind     emitter::emitInsToJumpKind(instruction ins)
{
    for (unsigned i = 0; i < ArrLen(emitJumpKindInstructions); i++)
    {
        if (ins == emitJumpKindInstructions[i])
        {
            emitJumpKind ret = (emitJumpKind)i;
            assert(EJ_NONE < ret && ret < EJ_COUNT);
            return ret;
        }
    }
    unreached();
}

/*****************************************************************************
 * Reverse the conditional jump
 */

/*static*/ emitJumpKind     emitter::emitReverseJumpKind(emitJumpKind jumpKind)
{
    assert(jumpKind < EJ_COUNT);
    return emitReverseJumpKinds[jumpKind];
}

/*****************************************************************************
 * Look up the condition code for a give jump kind
 */

/*static*/ unsigned         emitter::emitJumpKindCondCode(emitJumpKind jumpKind)
{
    assert(EJ_NONE < jumpKind && jumpKind < EJ_COUNT);
    return emitJumpKindCondCodes[jumpKind];
}

/*****************************************************************************
 *
 *  Return the allocated size (in bytes) of the given instruction descriptor.
 */

size_t              emitter::emitSizeOfInsDsc(instrDesc *id)
{
    assert (!emitIsTinyInsDsc(id));

    if  (emitIsScnsInsDsc(id))
        return SMALL_IDSC_SIZE;

    assert((unsigned)id->idInsFmt() < emitFmtCount);

    ID_OPS idOp         = (ID_OPS) emitFmtToOps[id->idInsFmt()];
    bool   isCallIns    = (id->idIns() == INS_bl) || (id->idIns() == INS_blx);
    bool   maybeCallIns = (id->idIns() == INS_b) || (id->idIns() == INS_bx);

    // An INS_call instruction may use a "fat" direct/indirect call descriptor
    // except for a local call to a label (i.e. call to a finally).
    // Only ID_OP_CALL and ID_OP_SPEC check for this, so we enforce that the
    // INS_call instruction always uses one of these idOps.

    assert(!isCallIns            ||     // either not a call or
           idOp == ID_OP_CALL    ||     // is a direct call
           idOp == ID_OP_SPEC    ||     // is an indirect call
           idOp == ID_OP_JMP       );   // is a local call to finally clause

    switch (idOp)
    {
    case ID_OP_NONE:
        break;

    case ID_OP_JMP:
        return  sizeof(instrDescJmp);

    case ID_OP_LBL:
        return  sizeof(instrDescLbl);

    case ID_OP_CALL:
    case ID_OP_SPEC:
        assert(isCallIns || maybeCallIns);
        if  (id->idIsLargeCall())
        {
            /* Must be a "fat" indirect call descriptor */
            return  sizeof(instrDescCGCA);
        }
        else
        {
            assert(!id->idIsLargeDsp());
            assert(!id->idIsLargeCns());
            return sizeof(instrDesc);
        }
        break;

    default:
        NO_WAY("unexpected instruction descriptor format");
        break;
    }

    if (id->idIsLargeCns())
    {
        if (id->idIsLargeDsp())
            return sizeof(instrDescCnsDsp);
        else
            return sizeof(instrDescCns);
    }
    else
    {
        if (id->idIsLargeDsp())
            return sizeof(instrDescDsp);
        else
            return sizeof(instrDesc);
    }
}


bool offsetFitsInVectorMem(int disp)
{
    unsigned imm = unsigned_abs(disp);
    return ((imm & 0x03fc) == imm);
}

#ifdef  DEBUG
/*****************************************************************************
 *
 *  The following called for each recorded instruction -- use for debugging.
 */
void                emitter::emitInsSanityCheck(instrDesc *id)
{
    /* What instruction format have we got? */

    switch (id->idInsFmt())
    {
    case IF_T1_A:    // T1_A    ................  
    case IF_T2_A:    // T2_A    ................ ................                       
        break;

    case IF_T1_B:    // T1_B    ........cccc....                                           cond 
    case IF_T2_B:    // T2_B    ................ ............iiii                          imm4 
        assert(emitGetInsSC(id) < 0x10);
        break; 

    case IF_T1_C:    // T1_C    .....iiiiinnnddd                       R1  R2              imm5   
        assert(isLowRegister(id->idReg1()));
        assert(isLowRegister(id->idReg2()));
        if (emitInsIsLoadOrStore(id->idIns()))
        {
            emitAttr  size = id->idOpSize();
            int       imm  = emitGetInsSC(id);
            
            imm  = insUnscaleImm(imm, size);
            assert(imm < 0x20);
        }
        else
        {
            assert(id->idSmallCns() < 0x20);
        }
        break;

    case IF_T1_D0:   // T1_D0   ........Dmmmmddd                       R1* R2*    
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        break;

    case IF_T1_D1:   // T1_D1   .........mmmm...                       R1*   
        assert(isGeneralRegister(id->idReg1()));
        break;

    case IF_T1_D2:   // T1_D2   .........mmmm...                               R3*
        assert(isGeneralRegister(id->idReg3()));
        break;

    case IF_T1_E:    // T1_E    ..........nnnddd                       R1  R2 
        assert(isLowRegister(id->idReg1()));
        assert(isLowRegister(id->idReg2()));
        assert(id->idSmallCns() < 0x20);
        break;

    case IF_T1_F:    // T1_F    .........iiiiiii                       SP                  imm7     
        assert(id->idReg1() == REG_SP);
        assert(id->idOpSize() == EA_4BYTE);
        assert((emitGetInsSC(id) & ~0x1FC) == 0);
        break;

    case IF_T1_G:    // T1_G    .......iiinnnddd                       R1  R2              imm3 
        assert(isLowRegister(id->idReg1()));
        assert(isLowRegister(id->idReg2()));
        assert(id->idSmallCns() < 0x8);
        break;

    case IF_T1_H:    // T1_H    .......mmmnnnddd                       R1  R2  R3 
        assert(isLowRegister(id->idReg1()));
        assert(isLowRegister(id->idReg2()));
        assert(isLowRegister(id->idReg3()));
        break;

    case IF_T1_I:    // T1_I    ......i.iiiiiddd                       R1                  imm6                
        assert(isLowRegister(id->idReg1()));
        break;

    case IF_T1_J0:   // T1_J0   .....dddiiiiiiii                       R1                  imm8 
        assert(isLowRegister(id->idReg1()));
        assert(emitGetInsSC(id) < 0x100);
        break;

    case IF_T1_J1:   // T1_J1   .....dddiiiiiiii                       R1                  <regmask8>  
        assert(isLowRegister(id->idReg1()));
        assert(emitGetInsSC(id) < 0x100);
        break;

    case IF_T1_J2:   // T1_J2   .....dddiiiiiiii                       R1  SP              imm8  
        assert(isLowRegister(id->idReg1()));
        assert(id->idReg2() == REG_SP);
        assert(id->idOpSize() == EA_4BYTE);
        assert((emitGetInsSC(id)& ~0x3FC) == 0);
        break;

    case IF_T1_L0:   // T1_L0   ........iiiiiiii                                           imm8
        assert(emitGetInsSC(id) < 0x100);
        break;

    case IF_T1_L1:   // T1_L1   .......Rrrrrrrrr                                           <regmask8+2> 
        assert(emitGetInsSC(id) < 0x400);
        break;

    case IF_T2_C0:   // T2_C0   ...........Snnnn .iiiddddiishmmmm       R1  R2  R3      S, imm5, sh 
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(emitGetInsSC(id) < 0x20);
        break;

    case IF_T2_C4:   // T2_C4   ...........Snnnn ....dddd....mmmm       R1  R2  R3      S 
    case IF_T2_C5:   // T2_C5   ............nnnn ....dddd....mmmm       R1  R2  R3    
    case IF_T2_G1:   // T2_G1   ............nnnn ttttTTTT........       R1  R2  R3  
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        break;

    case IF_T2_C1:   // T2_C1   ...........S.... .iiiddddiishmmmm       R1  R2          S, imm5, sh   
    case IF_T2_C2:   // T2_C2   ...........S.... .iiiddddii..mmmm       R1  R2          S, imm5 
    case IF_T2_C8:   // T2_C8   ............nnnn .iii....iishmmmm       R1  R2             imm5, sh  
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(emitGetInsSC(id) < 0x20);
        break;

    case IF_T2_C6:   // T2_C6   ................ ....dddd..iimmmm       R1  R2                   imm2
    case IF_T2_C7:   // T2_C7   ............nnnn ..........shmmmm       R1  R2                   imm2  
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(emitGetInsSC(id) < 0x4);
        break;

    case IF_T2_C3:   // T2_C3   ...........S.... ....dddd....mmmm       R1  R2          S 
    case IF_T2_C9:   // T2_C9   ............nnnn ............mmmm       R1  R2  
    case IF_T2_C10:  // T2_C10  ............mmmm ....dddd....mmmm       R1  R2     
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        break;

    case IF_T2_D0:   // T2_D0   ............nnnn .iiiddddii.wwwww       R1  R2             imm5, imm5
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(emitGetInsSC(id) < 0x400);
        break;

    case IF_T2_D1:   // T2_D1   ................ .iiiddddii.wwwww       R1                 imm5, imm5  
        assert(isGeneralRegister(id->idReg1()));
        assert(emitGetInsSC(id) < 0x400);
        break;

    case IF_T2_E0:   // T2_E0   ............nnnn tttt......shmmmm       R1  R2  R3               imm2
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        if (id->idIsLclVar())
        {
            assert(isGeneralRegister(codeGen->rsGetRsvdReg()));
        }
        else
        {
            assert(isGeneralRegister(id->idReg3()));
            assert(emitGetInsSC(id) < 0x4);
        }
        break;

    case IF_T2_E1:   // T2_E1   ............nnnn tttt............       R1  R2  
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        break;

    case IF_T2_E2:   // T2_E2   ................ tttt............       R1   
        assert(isGeneralRegister(id->idReg1()));
        break;

    case IF_T2_F1:    // T2_F1    ............nnnn ttttdddd....mmmm       R1  R2  R3  R4  
    case IF_T2_F2:    // T2_F2    ............nnnn aaaadddd....mmmm       R1  R2  R3  R4    
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(isGeneralRegister(id->idReg4()));
        break;

    case IF_T2_G0:   // T2_G0   .......PU.W.nnnn ttttTTTTiiiiiiii       R1  R2  R3         imm8, PUW  
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isGeneralRegister(id->idReg3()));
        assert(unsigned_abs(emitGetInsSC(id)) < 0x100);
        break;

    case IF_T2_H0:   // T2_H0   ............nnnn tttt.PUWiiiiiiii       R1  R2             imm8, PUW  
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(unsigned_abs(emitGetInsSC(id)) < 0x100);
        break;

    case IF_T2_H1:   // T2_H1   ............nnnn tttt....iiiiiiii       R1  R2             imm8 
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(emitGetInsSC(id) < 0x100);
        break;

    case IF_T2_H2:   // T2_H2   ............nnnn ........iiiiiiii       R1                 imm8  
        assert(isGeneralRegister(id->idReg1()));
        assert(emitGetInsSC(id) < 0x100);
        break;

    case IF_T2_I0:   // T2_I0   ..........W.nnnn rrrrrrrrrrrrrrrr       R1              W, imm16  
        assert(isGeneralRegister(id->idReg1()));
        assert(emitGetInsSC(id) < 0x10000);
        break;

    case IF_T2_N:    // T2_N    .....i......iiii .iiiddddiiiiiiii       R1                 imm16  
        assert(isGeneralRegister(id->idReg1()));
        break;

    case IF_T2_N2:   // T2_N2   .....i......iiii .iiiddddiiiiiiii       R1                 imm16  
        assert(isGeneralRegister(id->idReg1()));
        assert((size_t)emitGetInsSC(id) < emitDataSize());
        break;

    case IF_T2_I1:   // T2_I1   ................ rrrrrrrrrrrrrrrr                          imm16
        assert(emitGetInsSC(id) < 0x10000);
        break;

    case IF_T2_K1:   // T2_K1   ............nnnn ttttiiiiiiiiiiii       R1  R2             imm12
    case IF_T2_M0:   // T2_M0   .....i......nnnn .iiiddddiiiiiiii       R1  R2             imm12
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(emitGetInsSC(id) < 0x1000);
        break;

    case IF_T2_L0:   // T2_L0   .....i.....Snnnn .iiiddddiiiiiiii       R1  R2          S, imm8<<imm4 
        assert(isGeneralRegister(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(isModImmConst(emitGetInsSC(id)));
        break;

    case IF_T2_K4:   // T2_K4   ........U....... ttttiiiiiiiiiiii       R1  PC          U, imm12  
    case IF_T2_M1:   // T2_M1   .....i.......... .iiiddddiiiiiiii       R1  PC             imm12
        assert(isGeneralRegister(id->idReg1()));
        assert(id->idReg2() == REG_PC);
        assert(emitGetInsSC(id) < 0x1000);
        break;

    case IF_T2_K3:   // T2_K3   ........U....... ....iiiiiiiiiiii       PC              U, imm12 
        assert(id->idReg1() == REG_PC);
        assert(emitGetInsSC(id) < 0x1000);
        break;

    case IF_T2_K2:   // T2_K2   ............nnnn ....iiiiiiiiiiii       R1                 imm12  
        assert(isGeneralRegister(id->idReg1()));
        assert(emitGetInsSC(id) < 0x1000);
        break;

    case IF_T2_L1:   // T2_L1   .....i.....S.... .iiiddddiiiiiiii       R1              S, imm8<<imm4   
    case IF_T2_L2:   // T2_L2   .....i......nnnn .iii....iiiiiiii       R1                 imm8<<imm4  
        assert(isGeneralRegister(id->idReg1()));
        assert(isModImmConst(emitGetInsSC(id)));
        break;

    case IF_T1_J3:   // T1_J3   .....dddiiiiiiii                        R1  PC             imm8 
        assert(isGeneralRegister(id->idReg1()));
        assert(id->idReg2() == REG_PC);
        assert(emitGetInsSC(id) < 0x100);
        break;

    case IF_T1_K:    // T1_K    ....cccciiiiiiii                        Branch             imm8, cond4                            
    case IF_T1_M:    // T1_M    .....iiiiiiiiiii                        Branch             imm11                             
    case IF_T2_J1:   // T2_J1   .....Scccciiiiii ..j.jiiiiiiiiiii       Branch             imm20, cond4                
    case IF_T2_J2:   // T2_J2   .....Siiiiiiiiii ..j.jiiiiiiiiii.       Branch             imm24  
    case IF_T2_N1:   // T2_N    .....i......iiii .iiiddddiiiiiiii       R1                 imm16  
    case IF_T2_J3:   // T2_J3   .....Siiiiiiiiii ..j.jiiiiiiiiii.       Call               imm24    
    case IF_LARGEJMP: 
        break;

    case IF_T2_VFP3:
        if (id->idOpSize() == EA_8BYTE)
        {
            assert(isDoubleReg(id->idReg1()));
            assert(isDoubleReg(id->idReg2()));
            assert(isDoubleReg(id->idReg3()));
        }
        else
        {
            assert(id->idOpSize() == EA_4BYTE);
            assert(isFloatReg(id->idReg1()));
            assert(isFloatReg(id->idReg2()));
            assert(isFloatReg(id->idReg3()));
        }
        break;

    case IF_T2_VFP2:
        assert(isFloatReg(id->idReg1()));
        assert(isFloatReg(id->idReg2()));
        break;
    
    case IF_T2_VLDST: 
        if (id->idOpSize() == EA_8BYTE)
            assert(isDoubleReg(id->idReg1()));
        else
            assert(isFloatReg(id->idReg1()));
        assert(isGeneralRegister(id->idReg2()));
        assert(offsetFitsInVectorMem(emitGetInsSC(id))); 
        break;

    case IF_T2_VMOVD:
        assert(id->idOpSize() == EA_8BYTE);
        if (id->idIns() == INS_vmov_d2i)
        {
            assert(isGeneralRegister(id->idReg1()));
            assert(isGeneralRegister(id->idReg2()));
            assert(isDoubleReg(id->idReg3()));
        }
        else
        {
            assert(id->idIns() == INS_vmov_i2d);
            assert(isDoubleReg(id->idReg1()));
            assert(isGeneralRegister(id->idReg2()));
            assert(isGeneralRegister(id->idReg3()));
        }
        break;

    case IF_T2_VMOVS:
        assert(id->idOpSize() == EA_4BYTE);
        if (id->idIns() == INS_vmov_i2f)
        {
            assert(isFloatReg(id->idReg1()));
            assert(isGeneralRegister(id->idReg2()));
        }
        else 
        {
            assert(id->idIns() == INS_vmov_f2i);
            assert(isGeneralRegister(id->idReg1()));
            assert(isFloatReg(id->idReg2()));
        }
        break;

    default:
        printf("unexpected format %s\n", emitIfName(id->idInsFmt()));
        assert(!"Unexpected format");
        break;
    }
}
#endif  // DEBUG

bool           emitter::emitInsMayWriteToGCReg(instrDesc *id)
{
    instruction     ins  = id->idIns();
    insFormat       fmt  = id->idInsFmt();

    switch (fmt)
    {

    // These are the formats with "destination" or "target" registers:
    case IF_T1_C: case IF_T1_D0: case IF_T1_E: case IF_T1_G: case IF_T1_H: 
    case IF_T1_J0: case IF_T1_J1: case IF_T1_J2: case IF_T1_J3:
    case IF_T2_C0: case IF_T2_C1: case IF_T2_C2: case IF_T2_C3: case IF_T2_C4: case IF_T2_C5: case IF_T2_C6: case IF_T2_C10:
    case IF_T2_D0: case IF_T2_D1: case IF_T2_F1: case IF_T2_F2:
    case IF_T2_L0: case IF_T2_L1: case IF_T2_M0: case IF_T2_M1:
    case IF_T2_N: case IF_T2_N1: case IF_T2_N2: case IF_T2_VFP3: case IF_T2_VFP2: case IF_T2_VLDST: 
    case IF_T2_E0: case IF_T2_E1: case IF_T2_E2:
    case IF_T2_G0: case IF_T2_G1: case IF_T2_H0: case IF_T2_H1:
    case IF_T2_K1: case IF_T2_K4:
        // Some formats with "destination" or "target" registers are actually used for store instructions, for the "source" value
        // written to memory.
        // Similarly, PUSH has a target register, indicating the start of the set of registers to push.  POP
        // *does* write to at least one register, so we do not make that a special case.
        // Various compare/test instructions do not write (except to the flags). Technically "teq" does not need to be
        // be in this list because it has no forms matched above, but I'm putting it here for completeness.
        switch (ins)
        {
        case INS_str: case INS_strb: case INS_strh: case INS_strd:
        case INS_strex: case INS_strexb: case INS_strexd: case INS_strexh:
        case INS_push:
        case INS_cmp: case INS_cmn: case INS_tst: case INS_teq:
            return false;
        default:
            return true;
        }
    case IF_T2_VMOVS:
        // VMOV.i2f reads from the integer register. Conversely VMOV.f2i writes to GC pointer-sized
        // integer register that might have previously held GC pointers, so they need to be included.
        assert(id->idGCref() == GCT_NONE);
        return (ins == INS_vmov_f2i);

    case IF_T2_VMOVD:
        // VMOV.i2d reads from the integer registers. Conversely VMOV.d2i writes to GC pointer-sized
        // integer registers that might have previously held GC pointers, so they need to be included.
        assert(id->idGCref() == GCT_NONE);
        return (ins == INS_vmov_d2i);

    default:
        return false;
    }
}

bool           emitter::emitInsWritesToLclVarStackLoc(instrDesc *id)
{
    if (!id->idIsLclVar())
        return false;

    instruction     ins  = id->idIns();

    // This list is related to the list of instructions used to store local vars in emitIns_S_R().
    // We don't accept writing to float local vars.

    switch (ins)
    {
    case INS_strb: 
    case INS_strh:
    case INS_str: 
        return true;
    default:
        return false;
    }
}

bool           emitter::emitInsMayWriteMultipleRegs(instrDesc *id)
{
    instruction     ins  = id->idIns();

    switch (ins)
    {
    case INS_ldm: case INS_ldmdb:
    case INS_pop:
    case INS_smlal:
    case INS_smull:
    case INS_umlal:
    case INS_umull:
    case INS_vmov_d2i:
        return true;
    default:
        return false;
    }
}

/*****************************************************************************/
#ifdef  DEBUG
/*****************************************************************************
 *
 *  Return a string that represents the given register.
 */

const   char *      emitter::emitRegName(regNumber reg, emitAttr attr, bool varName)
{
    assert(reg < REG_COUNT);

    const   char *  rn = emitComp->compRegVarName(reg, varName, false);

    assert(strlen(rn) >= 1);

    return  rn;
}


const char *emitter::emitFloatRegName(regNumber reg, emitAttr attr, bool varName)
{
    assert(reg < REG_COUNT);

    const   char *  rn = emitComp->compRegVarName(reg, varName, true);

    assert(strlen(rn) >= 1);

    return  rn;
}
#endif // DEBUG

/*****************************************************************************
 *
 *  Returns the base encoding of the given CPU instruction.
 */

emitter::insFormat  emitter::emitInsFormat(instruction ins)
{
    const static insFormat insFormats[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                ) fmt,
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) fmt,
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) fmt,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) fmt,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) fmt,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) fmt,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) fmt,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) fmt,
        #include "instrs.h"
    };

    assert(ins < ArrLen(insFormats));
    assert((insFormats[ins] != IF_NONE));

    return  insFormats[ins];
}

// INST_FP is 1
#define   LD  2
#define   ST  4
#define   CMP 8

/*static*/ const BYTE CodeGenInterface::instInfo[] =
{
    #define INST1(id, nm, fp, ldst, fmt, e1                                ) ldst | INST_FP*fp,
    #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) ldst | INST_FP*fp,
    #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) ldst | INST_FP*fp,
    #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) ldst | INST_FP*fp,
    #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) ldst | INST_FP*fp,
    #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) ldst | INST_FP*fp,
    #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) ldst | INST_FP*fp,
    #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) ldst | INST_FP*fp,
    #include "instrs.h"
};

/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of load instruction
 */

bool  emitter::emitInsIsLoad(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & LD) ? true : false;
    else
        return false;
}

/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of compare or test instruction
 */

bool  emitter::emitInsIsCompare(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & CMP) ? true : false;
    else
        return false;
}

/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of store instruction
 */

bool  emitter::emitInsIsStore(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & ST) ? true : false;
    else
        return false;
}

/*****************************************************************************
 *
 *  Returns true if the instruction is some kind of load/store instruction
 */

bool  emitter::emitInsIsLoadOrStore(instruction ins)
{
    // We have pseudo ins like lea which are not included in emitInsLdStTab.
    if (ins < ArrLen(CodeGenInterface::instInfo))
        return (CodeGenInterface::instInfo[ins] & (LD|ST)) ? true : false;
    else
        return false;

}

#undef  LD
#undef  ST
#undef  CMP

/*****************************************************************************
 *
 *  Returns the specific encoding of the given CPU instruction and format
 */

size_t emitter::emitInsCode(instruction ins, insFormat fmt)
{
    const static size_t insCodes1[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                ) e1,
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) e1,
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) e1,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e1,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e1,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e1,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e1,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e1,
        #include "instrs.h"
    };
    const static size_t insCodes2[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            ) e2,
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) e2,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e2,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e2,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e2,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e2,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e2,
        #include "instrs.h"
    };
    const static size_t insCodes3[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        ) e3,
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e3,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e3,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e3,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e3,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e3,
        #include "instrs.h"
    };
    const static size_t insCodes4[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    ) e4,
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e4,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e4,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e4,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e4,
        #include "instrs.h"
    };
    const static size_t insCodes5[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                ) e5,
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e5,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e5,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e5,
        #include "instrs.h"
    };
    const static size_t insCodes6[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            ) e6,
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e6,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e6,
        #include "instrs.h"
    };
    const static size_t insCodes7[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            )
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e7,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e7,
        #include "instrs.h"
    };
    const static size_t insCodes8[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            )
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    ) e8,
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e8,
        #include "instrs.h"
    };
    const static size_t insCodes9[] =
    {
        #define INST1(id, nm, fp, ldst, fmt, e1                                )
        #define INST2(id, nm, fp, ldst, fmt, e1, e2                            )
        #define INST3(id, nm, fp, ldst, fmt, e1, e2, e3                        )
        #define INST4(id, nm, fp, ldst, fmt, e1, e2, e3, e4                    )
        #define INST5(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5                )
        #define INST6(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6            )
        #define INST8(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8    )
        #define INST9(id, nm, fp, ldst, fmt, e1, e2, e3, e4, e5, e6, e7, e8, e9) e9,
        #include "instrs.h"
    };
    const static insFormat formatEncode9[9]  = { IF_T1_D0, IF_T1_H,  IF_T1_J0, IF_T1_G,  IF_T2_L0, IF_T2_C0, IF_T1_F,  IF_T1_J2, IF_T1_J3 };
    const static insFormat formatEncode8[8]  = { IF_T1_H,  IF_T1_C,  IF_T2_E0, IF_T2_H0, IF_T2_K1, IF_T2_K4, IF_T1_J2, IF_T1_J3 };
    const static insFormat formatEncode6A[6] = { IF_T1_H,  IF_T1_C,  IF_T2_E0, IF_T2_H0, IF_T2_K1, IF_T2_K4};
    const static insFormat formatEncode6B[6] = { IF_T1_H,  IF_T1_C,  IF_T2_E0, IF_T2_H0, IF_T2_K1, IF_T1_J2 };
    const static insFormat formatEncode5A[5] = { IF_T1_E,  IF_T1_D0, IF_T1_J0, IF_T2_L1, IF_T2_C3 };
    const static insFormat formatEncode5B[5] = { IF_T1_E,  IF_T1_D0, IF_T1_J0, IF_T2_L2, IF_T2_C8 };
    const static insFormat formatEncode4A[4] = { IF_T1_E,  IF_T1_C,  IF_T2_C4, IF_T2_C2 };
    const static insFormat formatEncode4B[4] = { IF_T2_K2, IF_T2_H2, IF_T2_C7, IF_T2_K3 };
    const static insFormat formatEncode3A[3] = { IF_T1_E,  IF_T2_C0, IF_T2_L0 };
    const static insFormat formatEncode3B[3] = { IF_T1_E,  IF_T2_C8, IF_T2_L2 };
    const static insFormat formatEncode3C[3] = { IF_T1_E,  IF_T2_C1, IF_T2_L1 };
    const static insFormat formatEncode3D[3] = { IF_T1_L1, IF_T2_E2, IF_T2_I1 };
    const static insFormat formatEncode3E[3] = { IF_T2_N,  IF_T2_N1, IF_T2_N2 };
    const static insFormat formatEncode3F[3] = { IF_T1_M,  IF_T2_J2, IF_T2_J3 };
    const static insFormat formatEncode2A[2] = { IF_T1_K,  IF_T2_J1 };
    const static insFormat formatEncode2B[2] = { IF_T1_D1, IF_T1_D2 };
    const static insFormat formatEncode2C[2] = { IF_T1_D2, IF_T2_J3 };
    const static insFormat formatEncode2D[2] = { IF_T1_J1, IF_T2_I0 };
    const static insFormat formatEncode2E[2] = { IF_T1_E,  IF_T2_C6 };
    const static insFormat formatEncode2F[2] = { IF_T1_E,  IF_T2_C5 };
    const static insFormat formatEncode2G[2] = { IF_T1_J3, IF_T2_M1 };

    size_t    code   = BAD_CODE;
    insFormat insFmt = emitInsFormat(ins);
    bool      found  = false;
    int       index  = 0;

    switch (insFmt)
    {
    case IF_EN9:
        for (index=0; index<9; index++)
        {
            if (fmt == formatEncode9[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN8:        
        for (index=0; index<8; index++)
        {
            if (fmt == formatEncode8[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN6A:
        for (index=0; index<6; index++)
        {
            if (fmt == formatEncode6A[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN6B:
        for (index=0; index<6; index++)
        {
            if (fmt == formatEncode6B[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN5A:
        for (index=0; index<5; index++)
        {
            if (fmt == formatEncode5A[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN5B:
        for (index=0; index<5; index++)
        {
            if (fmt == formatEncode5B[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN4A:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4A[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN4B:
        for (index=0; index<4; index++)
        {
            if (fmt == formatEncode4B[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN3A:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3A[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN3B:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3B[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN3C:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3C[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN3D:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3D[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN3E:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3E[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN3F:
        for (index=0; index<3; index++)
        {
            if (fmt == formatEncode3F[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN2A:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2A[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN2B:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2B[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN2C:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2C[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN2D:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2D[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN2E:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2E[index])
            {
                found = true;
                break;
            }
        }
        break;
    case IF_EN2F:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2F[index])
            {
                found = true;
                break;
            }
        }
        break;

    case IF_EN2G:
        for (index=0; index<2; index++)
        {
            if (fmt == formatEncode2G[index])
            {
                found = true;
                break;
            }
        }
        break;

    default:
        index = 0;
        found = true;
        break;
    }

    assert(found);

    switch (index)
    {
    case 0:
        assert(ins < ArrLen(insCodes1));
        code = insCodes1[ins];
        break;
    case 1:
        assert(ins < ArrLen(insCodes2));
        code = insCodes2[ins];
        break;
    case 2:
        assert(ins < ArrLen(insCodes3));
        code = insCodes3[ins];
        break;
    case 3:
        assert(ins < ArrLen(insCodes4));
        code = insCodes4[ins];
        break;
    case 4:
        assert(ins < ArrLen(insCodes5));
        code = insCodes5[ins];
        break;
    case 5:
        assert(ins < ArrLen(insCodes6));
        code = insCodes6[ins];
        break;
    case 6:
        assert(ins < ArrLen(insCodes7));
        code = insCodes7[ins];
        break;
    case 7:
        assert(ins < ArrLen(insCodes8));
        code = insCodes8[ins];
        break;
    case 8:
        assert(ins < ArrLen(insCodes9));
        code = insCodes9[ins];
        break;
    }
    
    assert((code != BAD_CODE));

    return  code;
}

/*****************************************************************************
 *
 *  Return the code size of the given instruction format. The 'insSize' return type enum
 *  indicates a 16 bit, 32 bit, or 48 bit instruction.
 */
    
emitter::insSize   emitter::emitInsSize(insFormat  insFmt)
{
    if ((insFmt >= IF_T1_A) && (insFmt < IF_T2_A))
        return ISZ_16BIT;

    if ((insFmt >= IF_T2_A) && (insFmt < IF_INVALID))
        return ISZ_32BIT;

    if (insFmt == IF_LARGEJMP)
        return ISZ_48BIT;

    assert(!"Invalid insFormat");
    return ISZ_48BIT;
}

/*****************************************************************************
 *
 *  isModImmConst() returns true when immediate 'val32' can be encoded
 *   using the special modified immediate constant available in Thumb
 */

/*static*/ bool  emitter::isModImmConst(int val32)
{
    unsigned uval32 = (unsigned) val32;
    unsigned imm8   = uval32 & 0xff;

    /* encode = 0000x */
    if (imm8 == uval32)
        return true;

    unsigned imm32a = (imm8 << 16) | imm8;
    /* encode = 0001x */
    if (imm32a == uval32)
        return true;

    unsigned imm32b = (imm32a << 8);
    /* encode = 0010x */
    if (imm32b == uval32)
        return true;

    unsigned imm32c = (imm32a | imm32b);
    /* encode = 0011x */
    if (imm32c == uval32)
        return true;

    unsigned mask32 = 0x00000ff;

    unsigned encode = 31;  /* 11111 */
    unsigned temp;

    do {
        mask32 <<= 1;
        temp = uval32 & ~mask32;
        if (temp == 0)
            return true;
        encode--;
    } while (encode >= 8);

    return false;
}


/*****************************************************************************
 *
 *  encodeModImmConst() returns the special ARM 12-bit immediate encoding.
 *   that is used to encode the immediate.  (4-bits, 8-bits)
 *   If the imm can not be encoded then 0x0BADC0DE is returned.
 */

/*static*/ int emitter::encodeModImmConst(int val32)
{
    unsigned uval32 = (unsigned) val32;
    unsigned imm8   = uval32 & 0xff;
    unsigned encode = imm8 >> 7;

    /* encode = 0000x */
    if (imm8 == uval32)
    {
        goto DONE;
    }

    unsigned imm32a = (imm8 << 16) | imm8;
    /* encode = 0001x */
    if (imm32a == uval32)
    {
        encode += 2;
        goto DONE;
    }

    unsigned imm32b = (imm32a << 8);
    /* encode = 0010x */
    if (imm32b == uval32)
    {
        encode += 4;
        goto DONE;
    }

    unsigned imm32c = (imm32a | imm32b);
    /* encode = 0011x */
    if (imm32c == uval32)
    {
        encode +=6;
        goto DONE;
    }

    unsigned mask32 = 0x00000ff;
    unsigned temp;

    encode = 31;  /* 11111 */
    do {
        mask32 <<= 1;
        temp = uval32 & ~mask32;
        if (temp == 0)
        {
            imm8 = (uval32 & mask32) >> (32 - encode);
            assert((imm8 & 0x80) != 0);
            goto DONE;
        }
        encode--;
    } while (encode >= 8);

    assert(!"encodeModImmConst failed!");
    return BAD_CODE;

DONE:
    unsigned result = (encode << 7) | (imm8 & 0x7f);
    assert(result <= 0x0fff);
    assert(result >= 0);
    return (int) result;
}

/*****************************************************************************
 *
 *  emitIns_valid_imm_for_alu() returns true when the immediate 'imm' 
 *   can be encoded using the 12-bit funky Arm immediate encoding
 */
/*static*/ bool emitter::emitIns_valid_imm_for_alu(int imm)
{
    if (isModImmConst(imm))
        return true;
    return false;
}

/*****************************************************************************
 *
 *  emitIns_valid_imm_for_mov() returns true when the immediate 'imm' 
 *   can be encoded using a single mov or mvn instruction.
 */
/*static*/ bool emitter::emitIns_valid_imm_for_mov(int imm)
{
    if ((imm & 0x0000ffff) == imm)  // 16-bit immediate
        return true;
    if (isModImmConst(imm))         // funky arm immediate
        return true;
    if (isModImmConst(~imm))        // funky arm immediate via mvn
        return true;
    return false;
}

/*****************************************************************************
 *
 *  emitIns_valid_imm_for_small_mov() returns true when the immediate 'imm' 
 *   can be encoded using a single 2-byte mov instruction.
 */
/*static*/ bool emitter::emitIns_valid_imm_for_small_mov(regNumber reg, int imm, insFlags flags)
{
    return isLowRegister(reg) && insSetsFlags(flags) && ((imm & 0x00ff) == imm);
}

/*****************************************************************************
 *
 *  emitIns_valid_imm_for_add() returns true when the immediate 'imm' 
 *   can be encoded using a single add or sub instruction.
 */
/*static*/ bool emitter::emitIns_valid_imm_for_add(int imm, insFlags flags)
{
    if ((unsigned_abs(imm) <= 0x00000fff) && (flags != INS_FLAGS_SET))    // 12-bit immediate via add/sub
        return true;
    if (isModImmConst(imm))         // funky arm immediate
        return true;
    if (isModImmConst(-imm))        // funky arm immediate via sub
        return true;
    return false;
}

/*****************************************************************************
 *
 *  emitIns_valid_imm_for_add_sp() returns true when the immediate 'imm' 
 *   can be encoded in "add Rd,SP,i10".
 */
/*static*/ bool emitter::emitIns_valid_imm_for_add_sp(int imm)
{
    if ((imm & 0x03fc) == imm)
        return true;
    return false;
}

#ifdef ARM_HAZARD_AVOIDANCE
// This function is called whenever we about to emit an unconditional branch instruction
// that could be encoded using a T2 instruction 
// It returns true if we need to mark the instruction via idKraitNop(true)
//
bool emitter::emitKraitHazardActive(instrDesc * id)
{
    // Does the current instruction represent an 
    // unconditional branch instruction that is subject to the Krait errata
    //
    if (id->idIsKraitBranch())
    {
        // Only need to handle the Krait Hazard when we are Jitting
        //
        if ((emitComp->opts.eeFlags & CORJIT_FLG_PREJIT) == 0) 
        {
            // Have we seen the necessary number of T1 instructions?
            if (emitCurInstrCntT1 >= MAX_INSTR_COUNT_T1)
            {
                return true;    /* Assume that we need to add a nopw as well */
            }
        }
    }
    return false;
}

#endif

/*****************************************************************************
 *
 *  Add an instruction with no operands.
 */

void                emitter::emitIns(instruction ins)
{
    instrDesc *  id   = emitNewInstrSmall(EA_4BYTE);
    insFormat    fmt  = emitInsFormat(ins);
    insSize      isz  = emitInsSize(fmt);

    assert((fmt == IF_T1_A) || (fmt == IF_T2_A));

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction with a single immediate value.
 */

void                emitter::emitIns_I(instruction ins,
                                       emitAttr    attr,
                                       ssize_t     imm)
{
    insFormat  fmt    = IF_NONE;
    bool       hasLR  = false;
    bool       hasPC  = false;
    bool       useT2  = false;
    bool       onlyT1 = false;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
#ifdef FEATURE_ITINSTRUCTION
    case INS_it:
    case INS_itt:
    case INS_ite:
    case INS_ittt:
    case INS_itte:
    case INS_itet:
    case INS_itee:
    case INS_itttt:
    case INS_ittte:
    case INS_ittet:
    case INS_ittee:
    case INS_itett:
    case INS_itete:
    case INS_iteet:
    case INS_iteee:
        assert((imm & 0x0F) == imm);
        fmt  = IF_T1_B;
        attr = EA_4BYTE;
        break;
#endif // FEATURE_ITINSTRUCTION

    case INS_push:
        assert((imm & 0xA000) == 0);       // Cannot push PC or SP

        if (imm & 0x4000)                  // Is the LR being pushed?
            hasLR = true;

        goto COMMON_PUSH_POP;

    case INS_pop:
        assert((imm & 0x2000) == 0);       // Cannot pop SP
        assert((imm & 0xC000) != 0xC000);  // Cannot pop both PC and LR

        if (imm & 0x8000)                  // Is the PC being popped?
            hasPC = true;
        if (imm & 0x4000)                  // Is the LR being popped?
        {
            hasLR = true;
            useT2 = true;
        }

COMMON_PUSH_POP:

        if (((imm-1) & imm) == 0)          // Is only one or zero bits set in imm?
        {
            if (((imm == 0) && !hasLR) ||  // imm has no bits set, but hasLR is set
                (!hasPC && !hasLR)       ) // imm has one bit set, and neither of hasPC/hasLR are set
            {
                onlyT1 = true;             // if only one bit is set we must use the T1 encoding
            }
        }

        imm &= ~0xE000;  // ensure that PC, LR and SP bits are removed from imm

        if (((imm & 0x00ff) == imm) && !useT2)
        {
            fmt = IF_T1_L1;
        }
        else if (!onlyT1)
        {
            fmt = IF_T2_I1;
        }
        else
        {
            // We have to use the Thumb-2 push single register encoding
            regNumber reg = genRegNumFromMask(imm); 
            emitIns_R(ins, attr, reg);
            return;
        }

        // 
        // Encode the PC and LR bits as the lowest two bits 
        // 
        imm <<= 2;
        if (hasPC)
            imm |= 2;
        if (hasLR)
            imm |= 1;

        assert(imm != 0);

        break;

#if 0
    // TODO-ARM-Cleanup: Enable or delete.
    case INS_bkpt:   // Windows uses a different encoding
        if ((imm & 0x0000ffff) == imm)
        {
            fmt = IF_T1_L0;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;
#endif

    case INS_dmb:
    case INS_ism:
        if ((imm & 0x000f) == imm)
        {
            fmt  = IF_T2_B;
            attr = EA_4BYTE;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;
    }
    assert((fmt == IF_T1_B)  ||
           (fmt == IF_T1_L0) ||
           (fmt == IF_T1_L1) ||
           (fmt == IF_T2_I1) ||
           (fmt == IF_T2_B));

    instrDesc *  id  = emitNewInstrSC(attr, imm);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing a single register.
 */

void                emitter::emitIns_R(instruction ins,
                                       emitAttr    attr,
                                       regNumber   reg)
{
    emitAttr     size = EA_SIZE(attr);
    insFormat    fmt  = IF_NONE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_pop:
    case INS_push:
        if (isLowRegister(reg))
        {
            int regmask = 1 << ((int) reg);
            emitIns_I(ins, attr, regmask);
            return;
        }
        assert(size == EA_PTRSIZE);
        fmt = IF_T2_E2;
        break;

    case INS_vmrs:
        assert(size == EA_PTRSIZE);
        fmt = IF_T2_E2;
        break;

    case INS_bx:
        assert(size == EA_PTRSIZE);
        fmt = IF_T1_D1;
        break;
    case INS_rsb:
    case INS_mvn:
        emitIns_R_R_I(ins, attr, reg, reg, 0);
        return;

    }
    assert((fmt == IF_T1_D1) ||
           (fmt == IF_T2_E2));

    instrDesc *     id   = emitNewInstrSmall(attr);
    insSize         isz  = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing a register and a constant.
 */

void                emitter::emitIns_R_I(instruction ins,
                                         emitAttr    attr,
                                         regNumber   reg,
                                         int         imm,
                                         insFlags    flags /* = INS_FLAGS_DONT_CARE */)

{
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_add:
    case INS_sub:
        if ((reg == REG_SP) && insDoesNotSetFlags(flags) && ((imm & 0x01fc) == imm))
        {
            fmt = IF_T1_F;
            sf  = INS_FLAGS_NOT_SET;
        }
        else if (isLowRegister(reg) && insSetsFlags(flags) && (unsigned_abs(imm) <= 0x00ff))
        {
            if (imm < 0)
            {
                assert((ins == INS_add) || (ins == INS_sub));
                if (ins == INS_add)
                    ins = INS_sub;
                else // ins == INS_sub
                    ins = INS_add;
                imm = -imm;
            }
            fmt = IF_T1_J0;
            sf  = INS_FLAGS_SET;
        }
        else
        {
            // otherwise we have to use a Thumb-2 encoding
            emitIns_R_R_I(ins, attr, reg, reg, imm, flags);
            return;
        }
        break;

    case INS_adc:
        emitIns_R_R_I(ins, attr, reg, reg, imm, flags);
        return;

    case INS_vpush:
    case INS_vpop:
        assert(imm > 0);
        if (attr == EA_8BYTE)
        {
             assert(isDoubleReg(reg));
             assert(imm <= 16);
             imm *= 2;
        }
        else
        {
            assert(attr == EA_4BYTE);
            assert(isFloatReg(reg));
            assert(imm <= 16);
        }
        assert(((reg-REG_F0)+imm) <= 32);
        imm *= 4;

        if (ins == INS_vpush)
            imm = -imm;

        sf  = INS_FLAGS_NOT_SET;
        fmt = IF_T2_VLDST;
        break;
        
    case INS_stm:
        {
            sf  = INS_FLAGS_NOT_SET;
 
            bool hasLR  = false;
            bool hasPC  = false;
            bool useT2  = false;
            bool onlyT1 = false;

            assert((imm & 0x2000) == 0);       // Cannot pop SP
            assert((imm & 0xC000) != 0xC000);  // Cannot pop both PC and LR
            assert((imm & 0xFFFF0000) == 0);   // Can only contain lower 16 bits
                                               
            if (imm & 0x8000)                  // Is the PC being popped?
                hasPC = true;

            if (imm & 0x4000)                  // Is the LR being pushed?
            {
                hasLR = true;
                useT2 = true;
            }

            if (!isLowRegister(reg))
                useT2 = true;

            if (((imm-1) & imm) == 0)          // Is only one or zero bits set in imm?
            {
                if (((imm == 0) && !hasLR) ||  // imm has no bits set, but hasLR is set
                    (!hasPC && !hasLR)       ) // imm has one bit set, and neither of hasPC/hasLR are set
                {
                    onlyT1 = true;             // if only one bit is set we must use the T1 encoding
                }
            }

            imm &= ~0xE000;  // ensure that PC, LR and SP bits are removed from imm

            if (((imm & 0x00ff) == imm) && !useT2)
            {
                fmt = IF_T1_J1;
            }
            else if (!onlyT1)
            {
                fmt = IF_T2_I0;
            }
            else
            {
                assert(!"Instruction cannot be encoded");
                // We have to use the Thumb-2 str single register encoding
                // reg = genRegNumFromMask(imm);
                // emitIns_R(ins, attr, reg);
                return;
            }

            // 
            // Encode the PC and LR bits as the lowest two bits 
            // 
            if (fmt == IF_T2_I0)
            {
                imm <<= 2;
                if (hasPC)
                    imm |= 2;
                if (hasLR)
                    imm |= 1;
            }
            assert(imm != 0);
        }
        break;

    case INS_and:
    case INS_bic:
    case INS_eor:
    case INS_orr:
    case INS_orn:
    case INS_rsb:
    case INS_sbc:

    case INS_ror:
    case INS_asr:
    case INS_lsl:
    case INS_lsr:
        // use the Reg, Reg, Imm encoding
        emitIns_R_R_I(ins, attr, reg, reg, imm, flags);
        return;

    case INS_mov:
        assert(!EA_IS_CNS_RELOC(attr));

        if (isLowRegister(reg) && insSetsFlags(flags) && ((imm & 0x00ff) == imm))
        {
            fmt = IF_T1_J0;
            sf  = INS_FLAGS_SET;
        }
        else if (isModImmConst(imm))
        {
            fmt = IF_T2_L1;
            sf  = insMustSetFlags(flags);
        }
        else if (isModImmConst(~imm))   // See if we can use move negated instruction instead
        {
            ins = INS_mvn;
            imm = ~imm;
            fmt = IF_T2_L1;
            sf  = insMustSetFlags(flags);
        }
        else if (insDoesNotSetFlags(flags) && ((imm & 0x0000ffff) == imm))
        {
            // mov => movw instruction 
            ins = INS_movw;
            fmt = IF_T2_N;
            sf  = INS_FLAGS_NOT_SET;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    case INS_movw:
    case INS_movt:
        assert(insDoesNotSetFlags(flags));
        sf  = INS_FLAGS_NOT_SET;
        if ((imm & 0x0000ffff) == imm || EA_IS_RELOC(attr))
        {
            fmt = IF_T2_N;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    case INS_mvn:
        if (isModImmConst(imm))
        {
            fmt = IF_T2_L1;
            sf  = insMustSetFlags(flags);
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    case INS_cmp:
        assert(!EA_IS_CNS_RELOC(attr));
        assert(insSetsFlags(flags));
        sf  = INS_FLAGS_SET;
        if (isLowRegister(reg) && ((imm & 0x0ff) == imm))
        {
            fmt = IF_T1_J0;
        }
        else if (isModImmConst(imm))
        {
            fmt = IF_T2_L2;
        }
        else if (isModImmConst(-imm))
        {
            ins = INS_cmn;
            fmt = IF_T2_L2;
            imm = -imm;
        }
        else
        {
#ifndef LEGACY_BACKEND
            assert(!"emitIns_R_I: immediate doesn't fit into the instruction");
#else // LEGACY_BACKEND
            // Load val into a register
            regNumber valReg = codeGen->regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg));
            codeGen->instGen_Set_Reg_To_Imm(EA_PTRSIZE, valReg, (ssize_t)imm);
            emitIns_R_R(ins, attr, reg, valReg, flags);
#endif // LEGACY_BACKEND
            return;
        }
        break;

    case INS_cmn:
    case INS_tst:
    case INS_teq:
        assert(insSetsFlags(flags));
        sf  = INS_FLAGS_SET;
        if (isModImmConst(imm))
        {
            fmt = IF_T2_L2;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

#ifdef FEATURE_PLI_INSTRUCTION
    case INS_pli:
        assert(insDoesNotSetFlags(flags));
        if ((reg == REG_SP) && (unsigned_abs(imm) <= 0x0fff))
        {
            fmt = IF_T2_K3;
            sf  = INS_FLAGS_NOT_SET;
        }
         __fallthrough;
#endif // FEATURE_PLI_INSTRUCTION

    case INS_pld:
    case INS_pldw:
        assert(insDoesNotSetFlags(flags));
        sf  = INS_FLAGS_NOT_SET;
        if ((imm >= 0) && (imm <= 0x0fff))
        {
            fmt = IF_T2_K2;
        }
        else if ((imm < 0) && (-imm <= 0x00ff))
        {
            imm = -imm;
            fmt = IF_T2_H2;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;
    }
    assert((fmt == IF_T1_F ) ||
           (fmt == IF_T1_J0) ||
           (fmt == IF_T1_J1) ||
           (fmt == IF_T2_H2) ||
           (fmt == IF_T2_I0) ||
           (fmt == IF_T2_K2) || 
           (fmt == IF_T2_K3) ||
           (fmt == IF_T2_L1) ||
           (fmt == IF_T2_L2) || 
           (fmt == IF_T2_M1) ||
           (fmt == IF_T2_N ) ||
           (fmt == IF_T2_VLDST));

    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrSC(attr, imm);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing two registers
 */

void                emitter::emitIns_R_R(instruction ins,
                                         emitAttr    attr,
                                         regNumber   reg1,
                                         regNumber   reg2,
                                         insFlags    flags /* = INS_FLAGS_DONT_CARE */)

{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_add:
        if (insDoesNotSetFlags(flags))
        {
            fmt = IF_T1_D0;
            sf  = INS_FLAGS_NOT_SET;
            break;
        }
        __fallthrough;

    case INS_sub:
        // Use the Thumb-1 reg,reg,reg encoding
        emitIns_R_R_R(ins, attr, reg1, reg1, reg2, flags);
        return;

    case INS_mov:
        if (insDoesNotSetFlags(flags))
        {
            assert(reg1 != reg2);
            fmt = IF_T1_D0;
            sf  = INS_FLAGS_NOT_SET;
        }
        else // insSetsFlags(flags)
        {
            sf  = INS_FLAGS_SET;
            if (isLowRegister(reg1) && isLowRegister(reg2))
            {
                fmt = IF_T1_E;
            }
            else
            {
                fmt = IF_T2_C3;
            }
        }
        break;

    case INS_cmp:
        assert(insSetsFlags(flags));
        sf  = INS_FLAGS_SET;
        if (isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_E;    // both are low registers
        }
        else
        {
            fmt = IF_T1_D0;   // one or both are high registers
        }
        break;

    case INS_vmov_f2i:
        assert(isGeneralRegister(reg1));
        assert(isFloatReg(reg2));
        fmt = IF_T2_VMOVS;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_vmov_i2f:
        assert(isFloatReg(reg1));  
        assert(isGeneralRegister(reg2));
        fmt = IF_T2_VMOVS;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_vcvt_d2i:
    case INS_vcvt_d2u:
    case INS_vcvt_d2f:
        assert(isFloatReg(reg1));
        assert(isDoubleReg(reg2));
        goto VCVT_COMMON;

    case INS_vcvt_f2d:
    case INS_vcvt_u2d:
    case INS_vcvt_i2d:
        assert(isDoubleReg(reg1));
        assert(isFloatReg(reg2));
        goto VCVT_COMMON;

    case INS_vcvt_u2f: 
    case INS_vcvt_i2f: 
    case INS_vcvt_f2i:
    case INS_vcvt_f2u: 
        assert(size == EA_4BYTE);
        assert(isFloatReg(reg1));
        assert(isFloatReg(reg2));
        goto VCVT_COMMON;

    case INS_vmov:
        assert(reg1 != reg2);
        __fallthrough;

    case INS_vabs:
    case INS_vsqrt:
    case INS_vcmp:
    case INS_vneg:
        if (size == EA_8BYTE)
        {
            assert(isDoubleReg(reg1));
            assert(isDoubleReg(reg2));
        }
        else
        {
            assert(isFloatReg(reg1));
            assert(isFloatReg(reg2));
        }
        __fallthrough;

VCVT_COMMON:
        fmt = IF_T2_VFP2;
        sf = INS_FLAGS_NOT_SET;
        break;

    case INS_vadd:
    case INS_vmul:
    case INS_vsub:
    case INS_vdiv:
        emitIns_R_R_R(ins, attr, reg1, reg1, reg2);
        return;

    case INS_vldr:
    case INS_vstr:
    case INS_ldr:
    case INS_ldrb:
    case INS_ldrsb:
    case INS_ldrh:
    case INS_ldrsh:

    case INS_str:
    case INS_strb:
    case INS_strh:
        emitIns_R_R_I(ins, attr, reg1, reg2, 0);
        return;

    case INS_adc:
    case INS_and:
    case INS_bic:
    case INS_eor:
    case INS_orr:
    case INS_sbc:
        if (insSetsFlags(flags) && isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_E;
            sf  = INS_FLAGS_SET;
            break;
        }
        __fallthrough;

    case INS_orn:
        // assert below fired for bug 281892 where the two operands of an OR were
        // the same static field load which got cse'd. 
        // there's no reason why this assert would be true in general
        // assert(reg1 != reg2);
        // Use the Thumb-2 three register encoding
        emitIns_R_R_R_I(ins, attr, reg1, reg1, reg2, 0, flags);
        return;

    case INS_asr:
    case INS_lsl:
    case INS_lsr:
    case INS_ror:
        // assert below fired for bug 296394 where the two operands of an 
        // arithmetic right shift were the same local variable 
        // there's no reason why this assert would be true in general
        // assert(reg1 != reg2);
        if (insSetsFlags(flags) && isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_E;
            sf  = INS_FLAGS_SET;
        }
        else 
        {
            // Use the Thumb-2 three register encoding
            emitIns_R_R_R(ins, attr, reg1, reg1, reg2, flags);
            return;
        }
        break;

    case INS_mul:
        // We will prefer the T2 encoding, unless (flags == INS_FLAGS_SET)
        // The thumb-1 instruction executes much slower as it must always set the flags
        //
        if (insMustSetFlags(flags) && isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_E;
            sf  = INS_FLAGS_SET;
        }
        else 
        {
            // Use the Thumb-2 three register encoding
            emitIns_R_R_R(ins, attr, reg1, reg2, reg1, flags);
            return;
        }
        break;

    case INS_mvn:
    case INS_cmn:
    case INS_tst:
        if (insSetsFlags(flags) && isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_E;
            sf  = INS_FLAGS_SET;
        }
        else 
        {
            // Use the Thumb-2 register with shift encoding
            emitIns_R_R_I(ins, attr, reg1, reg2, 0, flags);
            return;
        }
        break;

    case INS_sxtb:
    case INS_uxtb:
        assert(size == EA_1BYTE);
        goto EXTEND_COMMON;

    case INS_sxth:
    case INS_uxth:
        assert(size == EA_2BYTE);
EXTEND_COMMON:
        assert(insDoesNotSetFlags(flags));
        if (isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_E;
            sf  = INS_FLAGS_NOT_SET;
        }
        else 
        {
            // Use the Thumb-2 reg,reg with rotation encoding
            emitIns_R_R_I(ins, attr, reg1, reg2, 0, INS_FLAGS_NOT_SET);
            return;
        }
        break;

    case INS_tbb:
        assert(size == EA_1BYTE);
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_C9;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_tbh:
        assert(size == EA_2BYTE);
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_C9;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_clz:
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_C10;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_ldrexb:
    case INS_strexb:
        assert(size == EA_1BYTE);
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_E1;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_ldrexh:
    case INS_strexh:
        assert(size == EA_2BYTE);
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_E1;
        sf  = INS_FLAGS_NOT_SET;
        break;
    default:
#ifdef DEBUG
        printf("did not expect instruction %s\n", codeGen->genInsName(ins));
#endif
        unreached();
    }

    assert((fmt == IF_T1_D0 ) ||
           (fmt == IF_T1_E  ) || 
           (fmt == IF_T2_C3 ) ||
           (fmt == IF_T2_C9 ) || 
           (fmt == IF_T2_C10) ||
           (fmt == IF_T2_VFP2)||
           (fmt == IF_T2_VMOVD)||
           (fmt == IF_T2_VMOVS)||
           (fmt == IF_T2_E1 )     );

    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrSmall(attr);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing a register and two constants.
 */

void                emitter::emitIns_R_I_I(instruction ins,
                                           emitAttr    attr,
                                           regNumber   reg,
                                           int         imm1,
                                           int         imm2,
                                           insFlags    flags /* = INS_FLAGS_DONT_CARE */)

{
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;
    int        imm  = 0;                        // combined immediates

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_bfc:
        {
            int lsb = imm1;
            int msb = lsb + imm2 - 1;

            assert((lsb >= 0) && (lsb <= 31));  // required for encoding of INS_bfc
            assert((msb >= 0) && (msb <= 31));  // required for encoding of INS_bfc
            assert(msb >= lsb);                 // required for encoding of INS_bfc

            imm = (lsb << 5) | msb;

            assert(insDoesNotSetFlags(flags));
            fmt = IF_T2_D1;
            sf  = INS_FLAGS_NOT_SET;
        }
        break;
    }
    assert(fmt == IF_T2_D1);
    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrSC(attr, imm);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idReg1(reg);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing two registers and a constant.
 */

void                emitter::emitIns_R_R_I(instruction ins,
                                           emitAttr    attr,
                                           regNumber   reg1,
                                           regNumber   reg2,
                                           int         imm,
                                           insFlags    flags /* = INS_FLAGS_DONT_CARE */,
                                           insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;

    if (ins == INS_lea)
    {
        ins = INS_add;
    }
    
    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_add:
        assert(insOptsNone(opt));

        // Can we possibly encode the immediate 'imm' using a Thumb-1 encoding?
        if ((reg2 == REG_SP) && insDoesNotSetFlags(flags) && ((imm & 0x03fc) == imm))
        {
            if ((reg1 == REG_SP) && ((imm & 0x01fc) == imm))
            {
                // Use Thumb-1 encoding
                emitIns_R_I(ins, attr, reg1, imm, flags);
                return;
            }
            else if (isLowRegister(reg1))
            {
                fmt = IF_T1_J2;
                sf  = INS_FLAGS_NOT_SET;
                break;
            }
        }
        __fallthrough;

    case INS_sub:
        assert(insOptsNone(opt));

        // Is it just a mov?
        if (imm == 0)
        {
            // Is the mov even necessary?
            // Fix 383915 ARM ILGEN
            if (reg1 != reg2)
            {
                emitIns_R_R(INS_mov, attr, reg1, reg2, flags);
            }
            return;
        }
        // Can we encode the immediate 'imm' using a Thumb-1 encoding?
        else if (isLowRegister(reg1) && isLowRegister(reg2) &&
                 insSetsFlags(flags) && (unsigned_abs(imm) <= 0x0007))
        {
            if (imm < 0)
            {
                assert((ins == INS_add) || (ins == INS_sub));
                if (ins == INS_add)
                    ins = INS_sub;
                else
                    ins = INS_add;
                imm = -imm;
            }
            fmt = IF_T1_G;
            sf  = INS_FLAGS_SET;
        }
        else if ((reg1 == reg2) && isLowRegister(reg1) &&
                 insSetsFlags(flags) && (unsigned_abs(imm) <= 0x00ff))
        {
            if (imm < 0)
            {
                assert((ins == INS_add) || (ins == INS_sub));
                if (ins == INS_add)
                    ins = INS_sub;
                else
                    ins = INS_add;
                imm = -imm;
            }
            // Use Thumb-1 encoding
            emitIns_R_I(ins, attr, reg1, imm, flags);
            return;
        }
        else if (isModImmConst(imm))
        {
            fmt = IF_T2_L0;
            sf  = insMustSetFlags(flags);
        }
        else if (isModImmConst(-imm))
        {
            assert((ins == INS_add) || (ins == INS_sub));
            ins = (ins == INS_add) ? INS_sub : INS_add;
            imm = -imm;
            fmt = IF_T2_L0;
            sf  = insMustSetFlags(flags);
        }
        else if (insDoesNotSetFlags(flags) && (unsigned_abs(imm) <= 0x0fff))
        {
            if (imm < 0)
            {
                assert((ins == INS_add) || (ins == INS_sub));
                ins = (ins == INS_add) ? INS_sub : INS_add;
                imm = -imm;
            }
            // add/sub => addw/subw instruction 
            // Note that even when using the w prefix the immediate is still only 12 bits?
            ins = (ins == INS_add) ? INS_addw : INS_subw;
            fmt = IF_T2_M0;
            sf  = INS_FLAGS_NOT_SET;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;
     
    case INS_and:
    case INS_bic:
    case INS_orr:
    case INS_orn:
        assert(insOptsNone(opt));
        if (isModImmConst(imm))
        {
            fmt = IF_T2_L0;
            sf  = insMustSetFlags(flags);
        }
        else if (isModImmConst(~imm))
        {
            fmt = IF_T2_L0;
            sf  = insMustSetFlags(flags);
            imm = ~imm;

            if (ins == INS_and)
                ins = INS_bic;
            else if (ins == INS_bic)
                ins = INS_and;
            else if (ins == INS_orr)
                ins = INS_orn;
            else if (ins == INS_orn)
                ins = INS_orr;
            else
                assert(!"Instruction cannot be encoded");
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    case INS_rsb:
        assert(insOptsNone(opt));
        if (imm == 0 && isLowRegister(reg1) && isLowRegister(reg2) && insSetsFlags(flags))
        {
            fmt = IF_T1_E;
            sf  = INS_FLAGS_SET;
            break;
        }
        __fallthrough;

    case INS_adc:
    case INS_eor:
    case INS_sbc:
        assert(insOptsNone(opt));
        if (isModImmConst(imm))
        {
            fmt = IF_T2_L0;
            sf  = insMustSetFlags(flags);
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    case INS_adr:
        assert(insOptsNone(opt));
        assert(insDoesNotSetFlags(flags));
        assert(reg2 == REG_PC);
        sf  = INS_FLAGS_NOT_SET;

        if (isLowRegister(reg1) && ((imm & 0x00ff) == imm))
        {
            fmt = IF_T1_J3;
        }
        else if ((imm & 0x0fff) == imm)
        {
            fmt = IF_T2_M1;
        }
        else
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    case INS_mvn:
        assert((imm >= 0) && (imm <= 31));      // required for encoding
        assert(!insOptAnyInc(opt));
        if (imm==0)
        {
            assert(insOptsNone(opt));
            if (isLowRegister(reg1) && isLowRegister(reg2) && insSetsFlags(flags))
            {
                // Use the Thumb-1 reg,reg encoding
                emitIns_R_R(ins, attr, reg1, reg2, flags);
                return;
            }
        }
        else // imm > 0  &&  imm <= 31
        {
            assert(insOptAnyShift(opt));
        }
        fmt = IF_T2_C1;
        sf  = insMustSetFlags(flags);
        break;

    case INS_cmp:
    case INS_cmn:
    case INS_teq:
    case INS_tst:
        assert(insSetsFlags(flags));
        assert((imm >= 0) && (imm <= 31));      // required for encoding
        assert(!insOptAnyInc(opt));
        if (imm==0) 
        {
            assert(insOptsNone(opt));
            if (ins == INS_cmp)
            {
                // Use the Thumb-1 reg,reg encoding
                emitIns_R_R(ins, attr, reg1, reg2, flags);
                return;
            }
            if (((ins == INS_cmn) || (ins == INS_tst)) &&
                isLowRegister(reg1) && isLowRegister(reg2))
            {
                // Use the Thumb-1 reg,reg encoding
                emitIns_R_R(ins, attr, reg1, reg2, flags);
                return;
            }
        }
        else // imm > 0  &&  imm <= 31)
        {
            assert(insOptAnyShift(opt));
            if (insOptsRRX(opt))
                assert(imm==1);
        }

        fmt = IF_T2_C8;
        sf  = INS_FLAGS_SET;
        break;

    case INS_ror:
    case INS_asr:
    case INS_lsl:
    case INS_lsr:
        assert(insOptsNone(opt));

        // On ARM, the immediate shift count of LSL and ROR must be between 1 and 31. For LSR and ASR, it is between
        // 1 and 32, though we don't ever use 32. Although x86 allows an immediate shift count of 8-bits in instruction
        // encoding, the CPU looks at only the lower 5 bits. As per ECMA, specifying a shift count to the IL SHR, SHL, or SHL.UN
        // instruction that is greater than or equal to the width of the type will yield an undefined value. We choose that
        // undefined value in this case to match x86 behavior, by only using the lower 5 bits of the constant shift count.
        imm &= 0x1f;

        if (imm == 0)
        {
            // Additional Fix 383915 ARM ILGEN
            if ((reg1 != reg2) || insMustSetFlags(flags))
            {
                // Use MOV/MOVS instriction
                emitIns_R_R(INS_mov, attr, reg1, reg2, flags);
            }
            return;
        }
        
        if (insSetsFlags(flags) && (ins != INS_ror) && 
            isLowRegister(reg1) && isLowRegister(reg2))
        {
            fmt = IF_T1_C;
            sf  = INS_FLAGS_SET;
        }
        else 
        {
            fmt = IF_T2_C2;
            sf  = insMustSetFlags(flags);
        }
        break;

    case INS_sxtb:
    case INS_uxtb:
        assert(size == EA_1BYTE);
        goto EXTEND_COMMON;

    case INS_sxth:
    case INS_uxth:
        assert(size == EA_2BYTE);
EXTEND_COMMON:
        assert(insOptsNone(opt));
        assert(insDoesNotSetFlags(flags));
        assert((imm & 0x018) == imm);  // required for encoding

        if ((imm == 0) && isLowRegister(reg1) && isLowRegister(reg2))
        {
            // Use Thumb-1 encoding
            emitIns_R_R(ins, attr, reg1, reg2, INS_FLAGS_NOT_SET);
            return;
        }

        fmt = IF_T2_C6;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_pld:
    case INS_pldw:
#ifdef FEATURE_PLI_INSTRUCTION
    case INS_pli:
#endif // FEATURE_PLI_INSTRUCTION
        assert(insOptsNone(opt));
        assert(insDoesNotSetFlags(flags));
        assert((imm & 0x003) == imm);  // required for encoding

        fmt = IF_T2_C7;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_ldrb:
    case INS_strb:
        assert(size == EA_1BYTE);
        assert(insDoesNotSetFlags(flags));

        if (isLowRegister(reg1) &&
            isLowRegister(reg2) && 
            insOptsNone(opt)    &&
            ((imm & 0x001f) == imm))
        {
            fmt = IF_T1_C;
            sf  = INS_FLAGS_NOT_SET;
            break;
        }
        goto COMMON_THUMB2_LDST;

    case INS_ldrsb:
        assert(size == EA_1BYTE);
        goto COMMON_THUMB2_LDST;

    case INS_ldrh:
    case INS_strh:
        assert(size == EA_2BYTE);
        assert(insDoesNotSetFlags(flags));

        if (isLowRegister(reg1) &&
            isLowRegister(reg2) && 
            insOptsNone(opt)    &&
            ((imm & 0x003e) == imm))
        {
            fmt = IF_T1_C;
            sf  = INS_FLAGS_NOT_SET;
            break;
        }
        goto COMMON_THUMB2_LDST;

    case INS_ldrsh:
        assert(size == EA_2BYTE);
        goto COMMON_THUMB2_LDST;

    case INS_vldr:
    case INS_vstr:
    case INS_vldm:
    case INS_vstm:
        assert(fmt == IF_NONE);
        assert(insDoesNotSetFlags(flags));
        assert(offsetFitsInVectorMem(imm));  // required for encoding
        if (insOptAnyInc(opt))
        {
            if (insOptsPostInc(opt))
            {
                assert(imm > 0);
            }
            else // insOptsPreDec(opt)
            {
                assert(imm < 0);
            }
        }
        else
        {
            assert(insOptsNone(opt));
        }

        sf  = INS_FLAGS_NOT_SET;
        fmt = IF_T2_VLDST;
        break;

    case INS_ldr:
    case INS_str:
        assert(size == EA_4BYTE);
        assert(insDoesNotSetFlags(flags));

        // Can we possibly encode the immediate 'imm' using a Thumb-1 encoding?
        if (isLowRegister(reg1) && insOptsNone(opt) && ((imm & 0x03fc) == imm))
        {
            if (reg2 == REG_SP)
            {
                fmt = IF_T1_J2;  
                sf  = INS_FLAGS_NOT_SET;
                break;
            }
            else if (reg2 == REG_PC)
            {
                if (ins == INS_ldr)
                {
                    fmt = IF_T1_J3;
                    sf  = INS_FLAGS_NOT_SET;
                    break;
                }
            }
            else if (isLowRegister(reg2))
            {
                // Only the smaller range 'imm' can be encoded
                if ((imm & 0x07c) == imm)
                {
                    fmt = IF_T1_C;
                    sf  = INS_FLAGS_NOT_SET;
                    break;
                }
            }
        }
        //
        // If we did not find a thumb-1 encoding above
        //
        __fallthrough;

COMMON_THUMB2_LDST:
        assert(fmt == IF_NONE);
        assert(insDoesNotSetFlags(flags));
        sf  = INS_FLAGS_NOT_SET;

        if (insOptAnyInc(opt))
        {
            if (insOptsPostInc(opt))
                assert(imm > 0);
            else // insOptsPreDec(opt)
                assert(imm < 0);

            if (unsigned_abs(imm) <= 0x00ff)
            {
                fmt = IF_T2_H0;
            }
            else 
            {
                assert(!"Instruction cannot be encoded");
            }
        }
        else {
            assert(insOptsNone(opt));
            if ((reg2 == REG_PC) && (unsigned_abs(imm) <= 0x0fff))
            {
                fmt = IF_T2_K4;
            }
            else if ((imm & 0x0fff) == imm)
            {
                fmt = IF_T2_K1;
            }
            else if (unsigned_abs(imm) <= 0x0ff)
            {
                fmt = IF_T2_H0;
            }
            else 
            {
                // Load imm into a register
                regNumber rsvdReg = codeGen->rsGetRsvdReg();
                codeGen->instGen_Set_Reg_To_Imm(EA_4BYTE, rsvdReg, (ssize_t)imm);
                emitIns_R_R_R(ins, attr, reg1, reg2, rsvdReg);
                return;
            }
        }
        break;

    case INS_ldrex:
    case INS_strex:
        assert(insOptsNone(opt));
        assert(insDoesNotSetFlags(flags));
        sf  = INS_FLAGS_NOT_SET;

        if ((imm & 0x03fc) == imm)
        {
            fmt = IF_T2_H0;
        }
        else 
        {
            assert(!"Instruction cannot be encoded");
        }
        break;

    default:
        assert(!"Unexpected instruction");
    }
    assert((fmt == IF_T1_C ) ||
           (fmt == IF_T1_E ) ||
           (fmt == IF_T1_G ) || 
           (fmt == IF_T1_J2) ||
           (fmt == IF_T1_J3) || 
           (fmt == IF_T2_C1) ||
           (fmt == IF_T2_C2) ||
           (fmt == IF_T2_C6) ||
           (fmt == IF_T2_C7) ||
           (fmt == IF_T2_C8) ||
           (fmt == IF_T2_H0) ||
           (fmt == IF_T2_H1) ||
           (fmt == IF_T2_K1) ||
           (fmt == IF_T2_K4) ||
           (fmt == IF_T2_L0) ||
           (fmt == IF_T2_M0) ||
           (fmt == IF_T2_VLDST) ||
           (fmt == IF_T2_M1)    );
    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrSC(attr, imm);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idInsOpt(opt);
    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing three registers.
 */

void                emitter::emitIns_R_R_R(instruction ins,
                                           emitAttr    attr,
                                           regNumber   reg1,
                                           regNumber   reg2,
                                           regNumber   reg3,
                                           insFlags    flags /* = INS_FLAGS_DONT_CARE */)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_add:
        // Encodings do not support SP in the reg3 slot
        if (reg3 == REG_SP)
        {
            // Swap reg2 and reg3
            reg3 = reg2;
            reg2 = REG_SP;
        }
        __fallthrough;

    case INS_sub:
        assert (reg3 != REG_SP);

        if (isLowRegister(reg1) &&
            isLowRegister(reg2) && 
            isLowRegister(reg3) && 
            insSetsFlags(flags)     )
        {
            fmt = IF_T1_H;
            sf  = INS_FLAGS_SET;
            break;
        }

        if ((ins == INS_add) && insDoesNotSetFlags(flags))
        {
            if (reg1 == reg2)
            {
                // Use the Thumb-1 regdest,reg encoding
                emitIns_R_R(ins, attr, reg1, reg3, flags);
                return;
            }
            if (reg1 == reg3)
            {
                // Use the Thumb-1 regdest,reg encoding
                emitIns_R_R(ins, attr, reg1, reg2, flags);
                return;
            }
        }

        // Use the Thumb-2 reg,reg,reg with shift encoding
        emitIns_R_R_R_I(ins, attr, reg1, reg2, reg3, 0, flags);
        return;

    case INS_adc:
    case INS_and:
    case INS_bic:
    case INS_eor:
    case INS_orr:
    case INS_sbc:
        if (reg1 == reg2)
        {
            // Try to encode as a Thumb-1 instruction
            emitIns_R_R(ins, attr, reg1, reg3, flags);
            return;
        }
        __fallthrough;

    case INS_orn:
        // Use the Thumb-2 three register encoding, with imm=0
        emitIns_R_R_R_I(ins, attr, reg1, reg2, reg3, 0, flags);
        return;

    case INS_asr:
    case INS_lsl:
    case INS_lsr:
        if (reg1 == reg2 && insSetsFlags(flags) && isLowRegister(reg1) && isLowRegister(reg3))
        {
            // Use the Thumb-1 regdest,reg encoding
            emitIns_R_R(ins, attr, reg1, reg3, flags);
            return;
        }
        __fallthrough;

    case INS_ror:
        fmt = IF_T2_C4;
        sf  = insMustSetFlags(flags);
        break;

    case INS_mul:
        if (insMustSetFlags(flags))
        {
            if ((reg1 == reg2) && isLowRegister(reg1))
            {
                // Use the Thumb-1 regdest,reg encoding
                emitIns_R_R(ins, attr, reg1, reg3, flags);
                return;
            }
            if ((reg1 == reg3) && isLowRegister(reg1))
            {
                // Use the Thumb-1 regdest,reg encoding
                emitIns_R_R(ins, attr, reg1, reg2, flags);
                return;
            }
            else 
            {
                assert(!"Instruction cannot be encoded");
            }   
        }
        __fallthrough;

    case INS_sdiv:
    case INS_udiv:
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_C5;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_ldrb:
    case INS_strb:
    case INS_ldrsb:
        assert(size == EA_1BYTE);
        goto COMMON_THUMB1_LDST;

    case INS_ldrsh:
    case INS_ldrh:
    case INS_strh:
        assert(size == EA_2BYTE);
        goto COMMON_THUMB1_LDST;

    case INS_ldr:
    case INS_str:
        assert(size == EA_4BYTE);

COMMON_THUMB1_LDST:
        assert(insDoesNotSetFlags(flags));

        if (isLowRegister(reg1) && isLowRegister(reg2) && isLowRegister(reg3))
        {
            fmt = IF_T1_H;
            sf  = INS_FLAGS_NOT_SET;
        }
        else
        {
            // Use the Thumb-2 reg,reg,reg with shift encoding
            emitIns_R_R_R_I(ins, attr, reg1, reg2, reg3, 0, flags);
            return;
        }
        break;

    case INS_vadd:
    case INS_vmul:
    case INS_vsub:
    case INS_vdiv:
        if (size == EA_8BYTE)
        {
            assert(isDoubleReg(reg1));
            assert(isDoubleReg(reg2));
            assert(isDoubleReg(reg3));
        }
        else
        {
            assert(isFloatReg(reg1));
            assert(isFloatReg(reg2));
            assert(isFloatReg(reg3));
        }
        fmt = IF_T2_VFP3;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_vmov_i2d:
        assert(isDoubleReg(reg1)); 
        assert(isGeneralRegister(reg2));
        assert(isGeneralRegister(reg3)); 
        fmt = IF_T2_VMOVD;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_vmov_d2i:
        assert(isGeneralRegister(reg1));
        assert(isGeneralRegister(reg2)); 
        assert(isDoubleReg(reg3)); 
        fmt = IF_T2_VMOVD;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_ldrexd:
    case INS_strexd:
        assert(insDoesNotSetFlags(flags));
        fmt = IF_T2_G1;
        sf  = INS_FLAGS_NOT_SET;
        break;


    default:
        unreached();
    }
    assert((fmt == IF_T1_H ) ||
           (fmt == IF_T2_C4) ||
           (fmt == IF_T2_C5) ||
           (fmt == IF_T2_VFP3) ||
           (fmt == IF_T2_VMOVD) ||
           (fmt == IF_T2_G1));
    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstr(attr);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing two registers and two constants.
 */

void                emitter::emitIns_R_R_I_I(instruction ins,
                                             emitAttr    attr,
                                             regNumber   reg1,
                                             regNumber   reg2,
                                             int         imm1,
                                             int         imm2,
                                             insFlags    flags /* = INS_FLAGS_DONT_CARE */)
{
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;

    int        lsb   = imm1;
    int        width = imm2;
    int        msb   = lsb + width - 1;
    int        imm  = 0;   /* combined immediate */

    assert((lsb   >= 0) && (lsb   <= 31));  // required for encodings 
    assert((width >  0) && (width <= 32));  // required for encodings
    assert((msb   >= 0) && (msb   <= 31));  // required for encodings
    assert(msb >= lsb);                     // required for encodings

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_bfi:
        assert(insDoesNotSetFlags(flags));
        imm = (lsb << 5) | msb;

        fmt = IF_T2_D0;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_sbfx:
    case INS_ubfx:
        assert(insDoesNotSetFlags(flags));
        imm = (lsb << 5) | (width-1);

        fmt = IF_T2_D0;
        sf  = INS_FLAGS_NOT_SET;
        break;
    }
    assert((fmt == IF_T2_D0));
    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrSC(attr, imm);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idReg1(reg1);
    id->idReg2(reg2);

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing three registers and a constant.
 */

void                emitter::emitIns_R_R_R_I(instruction ins,
                                             emitAttr    attr,
                                             regNumber   reg1,
                                             regNumber   reg2,
                                             regNumber   reg3,
                                             int         imm,
                                             insFlags    flags /* = INS_FLAGS_DONT_CARE */,
                                             insOpts     opt   /* = INS_OPTS_NONE */)
{
    emitAttr   size = EA_SIZE(attr);
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_DONT_CARE;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {

    case INS_add:
    case INS_sub:
        if (imm == 0)
        {
            if (isLowRegister(reg1) && 
                isLowRegister(reg2) && 
                isLowRegister(reg3) &&
                insSetsFlags(flags)     )
            {
                // Use the Thumb-1 reg,reg,reg encoding
                emitIns_R_R_R(ins, attr, reg1, reg2, reg3, flags);
                return;
            }
            if ((ins == INS_add) && insDoesNotSetFlags(flags))
            {
                if (reg1 == reg2)
                {
                    // Use the Thumb-1 regdest,reg encoding
                    emitIns_R_R(ins, attr, reg1, reg3, flags);
                    return;
                }
                if (reg1 == reg3)
                {
                    // Use the Thumb-1 regdest,reg encoding
                    emitIns_R_R(ins, attr, reg1, reg2, flags);
                    return;
                }
            }
        }
        __fallthrough;

    case INS_adc:
    case INS_and:
    case INS_bic:
    case INS_eor:
    case INS_orn:
    case INS_orr:
    case INS_sbc:
        assert((imm >= 0) && (imm <= 31));      // required for encoding
        assert(!insOptAnyInc(opt));
        if (imm==0)
        {
            if (opt == INS_OPTS_LSL)    // left shift of zero 
                opt = INS_OPTS_NONE;    //           is a nop

            assert(insOptsNone(opt));
            if (isLowRegister(reg1) && 
                isLowRegister(reg2) && 
                isLowRegister(reg3) &&
                insSetsFlags(flags)     )
            {
                if (reg1 == reg2)
                {
                    // Use the Thumb-1 regdest,reg encoding
                    emitIns_R_R(ins, attr, reg1, reg3, flags);
                    return;
                }
                if ((reg1 == reg3)   && 
                    (ins != INS_bic) &&
                    (ins != INS_orn) &&
                    (ins != INS_sbc)    )
                {
                    // Use the Thumb-1 regdest,reg encoding
                    emitIns_R_R(ins, attr, reg1, reg2, flags);
                    return;
                }
            }
        }
        else // imm > 0  &&  imm <= 31)
        {
            assert(insOptAnyShift(opt));
            if (insOptsRRX(opt))
                assert(imm==1);
        }
        fmt = IF_T2_C0;
        sf  = insMustSetFlags(flags);
        break;

    case INS_ldrb:
    case INS_ldrsb:
    case INS_strb:
        assert(size == EA_1BYTE);
        goto COMMON_THUMB2_LDST;

    case INS_ldrh:
    case INS_ldrsh:
    case INS_strh:
        assert(size == EA_2BYTE);
        goto COMMON_THUMB2_LDST;

    case INS_ldr:
    case INS_str:
        assert(size == EA_4BYTE);

COMMON_THUMB2_LDST:
        assert(insDoesNotSetFlags(flags));        
        assert((imm & 0x0003) == imm);  // required for encoding

        if ((imm == 0)          &&
            insOptsNone(opt)    &&
            isLowRegister(reg1) && 
            isLowRegister(reg2) && 
            isLowRegister(reg3))
        {
            // Use the Thumb-1 reg,reg,reg encoding
            emitIns_R_R_R(ins, attr, reg1, reg2, reg3, flags);
            return;
        }
        assert(insOptsNone(opt) || insOptsLSL(opt));
        fmt = IF_T2_E0;
        sf  = INS_FLAGS_NOT_SET;
        break;

    case INS_ldrd:
    case INS_strd:
        assert(insDoesNotSetFlags(flags));
        assert((imm & 0x03) == 0);
        sf  = INS_FLAGS_NOT_SET;

        if (insOptAnyInc(opt))
        {
            if (insOptsPostInc(opt))
                assert(imm > 0);
            else // insOptsPreDec(opt)
                assert(imm < 0);
        }
        else
        {
            assert(insOptsNone(opt));
        }

        if (unsigned_abs(imm) <= 0x03fc)
        {
            imm >>= 2;
            fmt = IF_T2_G0;
        }
        else 
        {
            assert(!"Instruction cannot be encoded");
        }   
        break;
    }
    assert((fmt == IF_T2_C0) ||
           (fmt == IF_T2_E0) ||
           (fmt == IF_T2_G0)     );
    assert(sf != INS_FLAGS_DONT_CARE);

    // 3-reg ops can't use the small instrdesc
    instrDescCns *id = emitAllocInstrCns(attr);
    id->idSetIsLargeCns();
    id->idcCnsVal  = imm;

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(emitInsSize(fmt));

    id->idInsFlags(sf);
    id->idInsOpt(opt);
    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);

#ifdef ARM_HAZARD_AVOIDANCE
    id->idKraitNop(emitKraitHazardActive(id));
#endif

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add an instruction referencing four registers.
 */

void                emitter::emitIns_R_R_R_R(instruction ins,
                                             emitAttr    attr,
                                             regNumber   reg1,
                                             regNumber   reg2,
                                             regNumber   reg3,
                                             regNumber   reg4)
{
    insFormat  fmt  = IF_NONE;
    insFlags   sf   = INS_FLAGS_NOT_SET;

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {

    case INS_smull:
    case INS_umull:
    case INS_smlal:
    case INS_umlal:
        assert(reg1 != reg2);   // Illegal encoding
        fmt = IF_T2_F1;
        break;
    case INS_mla:
    case INS_mls:
        fmt = IF_T2_F2;
        break;
    }
    assert((fmt == IF_T2_F1) || (fmt == IF_T2_F2));

    instrDesc *  id  = emitNewInstr(attr);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idReg3(reg3);
    id->idReg4(reg4);

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction with a static data member operand. If 'size' is 0, the
 *  instruction operates on the address of the static member instead of its
 *  value (e.g. "push offset clsvar", rather than "push dword ptr [clsvar]").
 */

void                emitter::emitIns_C   (instruction  ins,
                                          emitAttr     attr,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          int          offs)
{
    NYI("emitIns_C");
}


/*****************************************************************************
 *
 *  Add an instruction referencing stack-based local variable.
 */

void                emitter::emitIns_S   (instruction ins,
                                          emitAttr    attr,
                                          int         varx,
                                          int         offs)
{
    NYI("emitIns_S");
}


/*****************************************************************************
 *
 *  Add an instruction referencing a register and a stack-based local variable.
 */
void                emitter::emitIns_R_S (instruction ins,
                                          emitAttr    attr,
                                          regNumber   reg1,
                                          int         varx,
                                          int         offs)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Load() to select the correct instruction");
    }

    switch (ins)
    {
    case INS_add:
    case INS_ldr:
    case INS_ldrh:
    case INS_ldrb:
    case INS_ldrsh:
    case INS_ldrsb:
    case INS_vldr:
    case INS_vmov:
    case INS_movw:
    case INS_movt:
        break;

    case INS_lea:
        ins = INS_add;
        break;

    default:
        NYI("emitIns_R_S");
        return;
    }
    
    insFormat  fmt    = IF_NONE;
    insFlags   sf     = INS_FLAGS_NOT_SET;
    regNumber  reg2;

    /* Figure out the variable's frame position */
    int   base;
    int   disp;
    unsigned undisp;

    base   = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &reg2, offs);

    disp   = base + offs;
    undisp = unsigned_abs(disp);

    if (CodeGen::instIsFP(ins))
    {
        // all fp mem ops take 8 bit immediate, multiplied by 4, plus sign
        //
        // Note if undisp is not a multiple of four we will fail later on
        //   when we try to encode this instruction
        // Its better to fail later with a better error message than
        //   to fail here when the RBM_OPT_RSVD is not available
        //
        if (undisp <= 0x03fb)
        {
            fmt = IF_T2_VLDST;
        }
        else
        {
            regNumber rsvdReg = codeGen->rsGetRsvdReg();
            emitIns_genStackOffset(rsvdReg, varx, offs);
            emitIns_R_R(INS_add, EA_4BYTE, rsvdReg, reg2);
            emitIns_R_R_I(ins, attr, reg1, rsvdReg, 0);
            return;
        }
    }
    else if (emitInsIsLoadOrStore(ins))
    {
        if (isLowRegister(reg1)   &&
            (reg2 == REG_SP)      && 
            (ins == INS_ldr)      &&
            ((disp & 0x03fc) == disp && disp <= 0x03f8))
        {
            fmt = IF_T1_J2; 
        }
        else if (disp >= 0 && disp <= 0x0ffb)
        {
            fmt = IF_T2_K1;
        }
        else if (undisp <= 0x0fb)
        {
            fmt = IF_T2_H0;
        }
        else
        {
            // Load disp into a register          
            regNumber rsvdReg = codeGen->rsGetRsvdReg(); 
            emitIns_genStackOffset(rsvdReg, varx, offs);
            fmt = IF_T2_E0;
        }
    }
    else if (ins == INS_add)
    {
        if (isLowRegister(reg1) && 
            (reg2 == REG_SP) && 
            ((disp & 0x03fc) == disp && disp <= 0x03f8))
        {
            fmt = IF_T1_J2;
        }
        else if (undisp <= 0x0ffb)
        {
            if (disp < 0)
            {
                ins = INS_sub;
                disp = -disp;
            }
            // add/sub => addw/subw instruction 
            // Note that even when using the w prefix the immediate is still only 12 bits?
            ins = (ins == INS_add) ? INS_addw : INS_subw;
            fmt = IF_T2_M0;
        }
        else
        {
            // Load disp into a register
            regNumber rsvdReg = codeGen->rsGetRsvdReg();
            emitIns_genStackOffset(rsvdReg, varx, offs);
            emitIns_R_R_R(ins, attr, reg1, reg2, rsvdReg);
            return;
        }
    }
    else if (ins == INS_movw || ins == INS_movt)
    {
        fmt = IF_T2_N;
    }

    assert((fmt == IF_T1_J2) ||
           (fmt == IF_T2_E0) ||
           (fmt == IF_T2_H0) ||
           (fmt == IF_T2_K1) ||
           (fmt == IF_T2_L0) ||
           (fmt == IF_T2_N)  ||
           (fmt == IF_T2_VLDST) ||
           (fmt == IF_T2_M0));
    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrCns(attr, disp);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idInsOpt(INS_OPTS_NONE);
    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
    id->idSetIsLclVar();
    if (reg2 == REG_FP)
        id->idSetIsLclFPBase();

#ifdef DEBUG
    id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
#endif

    dispIns(id);
    appendToCurIG(id);
}

// generate the offset of &varx + offs into a register
void emitter::emitIns_genStackOffset(regNumber r, int varx, int offs)
{
    regNumber regBase;
    int   base;
    int   disp;

    base = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &regBase, offs);
    disp = base + offs;

    emitIns_R_S(INS_movw, EA_4BYTE, r, varx, offs);

    if ((disp & 0xffff) != disp)
    {
        emitIns_R_S(INS_movt, EA_4BYTE, r, varx, offs);
    }
}

/*****************************************************************************
 *
 *  Add an instruction referencing a stack-based local variable and a register
 */
void                emitter::emitIns_S_R (instruction ins,
                                          emitAttr    attr,
                                          regNumber   reg1,
                                          int         varx,
                                          int         offs)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Store() to select the correct instruction");
    }

    switch (ins)
    {
    case INS_str:
    case INS_strh:
    case INS_strb:
    case INS_vstr:
        break;

    default:
        NYI("emitIns_R_S");
        return;
    }

    insFormat  fmt    = IF_NONE;
    insFlags   sf     = INS_FLAGS_NOT_SET;
    regNumber  reg2;

    /* Figure out the variable's frame position */
    int   base;
    int   disp;
    unsigned undisp;

    base   = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &reg2, offs);

    disp   = base + offs;
    undisp = unsigned_abs(disp);

    if (CodeGen::instIsFP(ins))
    {
        // all fp mem ops take 8 bit immediate, multiplied by 4, plus sign
        //
        // Note if undisp is not a multiple of four we will fail later on
        //   when we try to encode this instruction
        // Its better to fail later with a better error message than
        //   to fail here when the RBM_OPT_RSVD is not available
        //
        if (undisp <= 0x03fb)
        {
            fmt = IF_T2_VLDST;
        }
        else
        {
            regNumber rsvdReg = codeGen->rsGetRsvdReg();
            emitIns_genStackOffset(rsvdReg, varx, offs);
            emitIns_R_R(INS_add, EA_4BYTE, rsvdReg, reg2);
            emitIns_R_R_I(ins, attr, reg1, rsvdReg, 0);
            return;
        }
    }
    else if (isLowRegister(reg1)   &&
             (reg2 == REG_SP)      && 
             (ins == INS_str)      &&
             ((disp & 0x03fc) == disp && disp <= 0x03f8))
    {
        fmt = IF_T1_J2;  
    }
    else if (disp >= 0 && disp <= 0x0ffb)
    {
        fmt = IF_T2_K1;
    }
    else if (undisp <= 0x0fb)
    {
        fmt = IF_T2_H0;
    }
    else
    {
        // Load disp into a register
        regNumber rsvdReg = codeGen->rsGetRsvdReg();
        emitIns_genStackOffset(rsvdReg, varx, offs);
        fmt = IF_T2_E0;
    }
    assert((fmt == IF_T1_J2) ||
           (fmt == IF_T2_E0) ||
           (fmt == IF_T2_H0) ||
           (fmt == IF_T2_VLDST) ||
           (fmt == IF_T2_K1)    );
    assert(sf != INS_FLAGS_DONT_CARE);

    instrDesc *  id  = emitNewInstrCns(attr, disp);
    insSize      isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);
    id->idInsFlags(sf);
    id->idInsOpt(INS_OPTS_NONE);
    id->idReg1(reg1);
    id->idReg2(reg2);
    id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
    id->idSetIsLclVar();
    if (reg2 == REG_FP)
        id->idSetIsLclFPBase();
#ifdef DEBUG
    id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
#endif

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add an instruction referencing stack-based local variable and an immediate
 */
void                emitter::emitIns_S_I (instruction ins,
                                          emitAttr    attr,
                                          int         varx,
                                          int         offs,
                                          int         val)
{
    NYI("emitIns_S_I");
}


/*****************************************************************************
 *
 *  Add an instruction with a register + static member operands.
 */
void                emitter::emitIns_R_C (instruction  ins,
                                          emitAttr     attr,
                                          regNumber    reg,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          int          offs)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Load() to select the correct instruction");
    }
    assert(emitInsIsLoad(ins) || (ins == INS_lea));
    if (ins == INS_lea)
    {
        ins = INS_add;
    }

    int       doff  = Compiler::eeGetJitDataOffs(fldHnd);
    ssize_t   addr  = NULL;

    if (doff >= 0)
    {
        NYI_ARM("JitDataOffset static fields");
    }
    else if (fldHnd == FLD_GLOBAL_FS)
    {
        NYI_ARM("Thread-Local-Storage static fields");
    }
    else if (fldHnd == FLD_GLOBAL_DS)
    {
        addr = (ssize_t) offs;
        offs = 0;
    }
    else
    {
        assert(!jitStaticFldIsGlobAddr(fldHnd));
        addr = (ssize_t)emitComp->info.compCompHnd->getFieldAddress(fldHnd, NULL);
        if (addr == NULL)
            NO_WAY("could not obtain address of static field");
    }

    // We can use reg to load the constant address, 
    //  as long as it is not a floating point register 
    regNumber regTmp = reg;
    
    if (isFloatReg(regTmp))
    {
#ifndef LEGACY_BACKEND
        assert(!"emitIns_R_C() cannot be called with floating point target");
#else // LEGACY_BACKEND
        regTmp = codeGen->regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg));
#endif // LEGACY_BACKEND
    }

    // Load address of CLS_VAR into a register
    codeGen->instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regTmp, addr);

    if ((ins != INS_add) || (offs != 0) || (reg != regTmp))
    {
        emitIns_R_R_I(ins, attr, reg, regTmp, offs);
    }
}


/*****************************************************************************
 *
 *  Add an instruction with a static member + register operands.
 */

void                emitter::emitIns_C_R (instruction  ins,
                                          emitAttr     attr,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          regNumber    reg,
                                          int          offs)
{
#ifndef LEGACY_BACKEND
    assert(!"emitIns_C_R not supported for RyuJIT backend");
#else // LEGACY_BACKEND
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Store() to select the correct instruction");
    }
    assert(emitInsIsStore(ins));

    int       doff  = Compiler::eeGetJitDataOffs(fldHnd);
    ssize_t   addr  = NULL;

    if (doff >= 0)
    {
        NYI_ARM("JitDataOffset static fields");
    }
    else if (fldHnd == FLD_GLOBAL_FS)
    {
        NYI_ARM("Thread-Local-Storage static fields");
    }
    else if (fldHnd == FLD_GLOBAL_DS)
    {
        addr = (ssize_t) offs;
        offs = 0;
    }
    else
    {
        assert(!jitStaticFldIsGlobAddr(fldHnd));
        addr = (ssize_t)emitComp->info.compCompHnd->getFieldAddress(fldHnd, NULL);
        if (addr == NULL)
            NO_WAY("could not obtain address of static field");
    }

    regNumber regTmp = codeGen->regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg));

    // Load address of CLS_VAR into a register
    codeGen->instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regTmp, addr);

    emitIns_R_R_I(ins, attr, reg, regTmp, offs);
#endif // LEGACY_BACKEND
}


/*****************************************************************************
 *
 *  Add an instruction with a static member + constant.
 */

void                emitter::emitIns_C_I (instruction  ins,
                                          emitAttr     attr,
                                          CORINFO_FIELD_HANDLE fldHnd,
                                          int          offs,
                                          ssize_t      val)
{
    NYI("emitIns_C_I");
}

/*****************************************************************************
 *
 *  The following adds instructions referencing address modes.
 */

void                emitter::emitIns_I_AR  (instruction ins,
                                            emitAttr    attr,
                                            int         val,
                                            regNumber   reg,
                                            int         offs,
                                            int         memCookie,
                                            void *      clsCookie)
{
    NYI("emitIns_I_AR");
}

void                emitter::emitIns_R_AR  (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            int         offs,
                                            int         memCookie /* = 0 */,
                                            void *      clsCookie /* = NULL */)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Load() to select the correct instruction");
    }

    if (ins == INS_lea)
    {
        if (emitter::emitIns_valid_imm_for_add(offs, INS_FLAGS_DONT_CARE))
        {
            emitIns_R_R_I(INS_add, attr, ireg, reg, offs);
        }
        else
        {
#ifndef LEGACY_BACKEND
            assert(!"emitIns_R_AR: immediate doesn't fit in the instruction");
#else // LEGACY_BACKEND
            // Load val into a register
            regNumber immReg = codeGen->regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(ireg) & ~genRegMask(reg) );
            codeGen->instGen_Set_Reg_To_Imm(EA_PTRSIZE, immReg, (ssize_t)offs);
            emitIns_R_R_R(INS_add, attr, ireg, reg, immReg);
#endif // LEGACY_BACKEND
        }
        return;
    }
    else if (emitInsIsLoad(ins))
    {
        emitIns_R_R_I(ins, attr, ireg, reg, offs);
        return;
    }
    else if ((ins == INS_mov) || (ins == INS_ldr))
    {
        if (EA_SIZE(attr) == EA_4BYTE)
        {
            emitIns_R_R_I(INS_ldr, attr, ireg, reg, offs);
            return;
        }
    }
    else if (ins == INS_vldr)
    {
        emitIns_R_R_I(ins, attr, ireg, reg, offs);
    }
    NYI("emitIns_R_AR");
}

void                emitter::emitIns_R_AI  (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            ssize_t     disp)
{
    if (emitInsIsLoad(ins))
    {
        // We can use ireg to load the constant address, 
        //  as long as it is not a floating point register 
        regNumber regTmp = ireg;
        
        if (isFloatReg(regTmp))
        {
#ifndef LEGACY_BACKEND
            assert(!"emitIns_R_AI with floating point reg");
#else // LEGACY_BACKEND
            regTmp = codeGen->regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(ireg));
#endif // LEGACY_BACKEND
        }

        codeGen->instGen_Set_Reg_To_Imm(EA_IS_RELOC(attr) ? EA_HANDLE_CNS_RELOC : EA_PTRSIZE, regTmp, disp);
        emitIns_R_R_I(ins, EA_TYPE(attr), ireg, regTmp, 0);
        return;
    }
    NYI("emitIns_R_AI");
}

void                emitter::emitIns_AR_R  (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            int         offs,
                                            int         memCookie /* = 0 */,
                                            void *      clsCookie /* = NULL */)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Store() to select the correct instruction");
    }
    emitIns_R_R_I(ins, attr, ireg, reg, offs);
}

void                emitter::emitIns_R_ARR (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            regNumber   rg2,
                                            int         disp)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Load() to select the correct instruction");
    }

    if (ins == INS_lea) 
    {
        emitIns_R_R_R(INS_add, attr, ireg, reg, rg2);
        if (disp != 0)
        {
            emitIns_R_R_I(INS_add, attr, ireg, ireg, disp);
        }
        return;
    }
    else if (emitInsIsLoad(ins))
    {
        if (disp == 0)
        {
            emitIns_R_R_R_I(ins, attr, ireg, reg, rg2, 0, INS_FLAGS_DONT_CARE, INS_OPTS_NONE);
            return;
        }        
    }
    assert(!"emitIns_R_ARR: Unexpected instruction");
}

void                emitter::emitIns_ARR_R (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            regNumber   rg2,
                                            int         disp)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Store() to select the correct instruction");
    }
    if (emitInsIsStore(ins))
    {
        if (disp == 0)
        {
            emitIns_R_R_R(ins, attr, ireg, reg, rg2);
        }
        else
        {
            emitIns_R_R_R(INS_add, attr, ireg, reg, rg2);
            emitIns_R_R_I(ins, attr, ireg, ireg, disp);
        }
        return;
    }
    assert(!"emitIns_ARR_R: Unexpected instruction");
}

void                emitter::emitIns_R_ARX (instruction ins,
                                            emitAttr    attr,
                                            regNumber   ireg,
                                            regNumber   reg,
                                            regNumber   rg2,
                                            unsigned    mul,
                                            int         disp)
{
    if (ins == INS_mov)
    {
        assert(!"Please use ins_Load() to select the correct instruction");
    }

    unsigned shift = genLog2((unsigned) mul);

    if ((ins == INS_lea) || emitInsIsLoad(ins))
    {
        if (ins == INS_lea) 
        {
            ins = INS_add;
        }
        if (disp == 0)
        {
            emitIns_R_R_R_I(ins, attr, ireg, reg, rg2, (int)shift, INS_FLAGS_DONT_CARE, INS_OPTS_LSL);
            return;
        }
        else
        {
            bool useForm2     = false;
            bool mustUseForm1 = ((disp % mul) != 0) || (reg == ireg);
            if (!mustUseForm1)
            {
                // If all of the below things are true we can generate a Thumb-1 add instruction
                //  followed by a Thumb-2 add instruction
                // We also useForm1 when reg is a low register since the second instruction 
                //  can then always be generated using a Thumb-1 add
                //
                if ((reg >= REG_R8) && (ireg < REG_R8) && (rg2 < REG_R8) && ((disp >> shift) <= 7))
                {
                    useForm2 = true;
                }
            }

            if (useForm2)
            {
                // Form2:
                //     Thumb-1   instruction    add     Rd, Rx, disp>>shift
                //     Thumb-2   instructions   ldr     Rd, Rb, Rd LSL shift
                //
                emitIns_R_R_I(INS_add, EA_4BYTE,  ireg,  rg2,  disp >> shift);
                emitIns_R_R_R_I(ins, attr,  ireg,  reg, 
                                            ireg, shift, 
                                            INS_FLAGS_NOT_SET, INS_OPTS_LSL);
            }
            else
            {
                // Form1:
                //     Thumb-2   instruction    add     Rd, Rb, Rx LSL shift
                //     Thumb-1/2 instructions   ldr     Rd, Rd, disp
                //
                emitIns_R_R_R_I(INS_add, attr,  ireg,  reg, 
                                                rg2,  shift, 
                                                INS_FLAGS_NOT_SET, INS_OPTS_LSL);
                emitIns_R_R_I(ins, attr,  ireg,  ireg, disp);
            }
            return;
        }
    }

    assert(!"emitIns_R_ARX: Unexpected instruction");
}

/*****************************************************************************
 *
 *  Record that a jump instruction uses the short encoding
 *
 */
void  emitter::emitSetShortJump(instrDescJmp  * id)
{
    if (id->idjKeepLong)
        return;

    if  (emitIsCondJump(id))
    {
        id->idInsFmt(IF_T1_K);
    }
    else if (emitIsCmpJump(id))
    {
        // These are always only ever short!
        assert(id->idjShort);
        return;
    }
    else if (emitIsUncondJump(id))
    {
        id->idInsFmt(IF_T1_M);
    }
    else if (emitIsLoadLabel(id))
    {
        return; // Keep long - we don't know the alignment of the target
    }
    else
    {
        assert(!"Unknown instruction in emitSetShortJump()");
    }

    id->idjShort = true;

#if DEBUG_EMIT
    if  (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
    {
        printf("[8] Converting jump %u to short\n", id->idDebugOnlyInfo()->idNum);
    }
#endif // DEBUG_EMIT

    insSize isz = emitInsSize(id->idInsFmt());
    id->idInsSize(isz);
}

/*****************************************************************************
 *
 *  Record that a jump instruction uses the medium encoding
 *
 */
void  emitter::emitSetMediumJump(instrDescJmp  * id)
{
    if (id->idjKeepLong)
        return;

#if DEBUG_EMIT
    if  (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
    {
        printf("[9] Converting jump %u to medium\n", id->idDebugOnlyInfo()->idNum);
    }
#endif // DEBUG_EMIT

    assert(emitIsCondJump(id));
    id->idInsFmt(IF_T2_J1);
    id->idjShort = false;

    insSize isz = emitInsSize(id->idInsFmt());
    id->idInsSize(isz);
}

/*****************************************************************************
 *
 *  Add a jmp instruction.
 *  When dst is NULL, instrCount specifies number of instructions
 *       to jump: positive is forward, negative is backward.
 *  Unconditional branches have two sizes: short and long.
 *  Conditional branches have three sizes: short, medium, and long. A long
 *     branch is a pseudo-instruction that represents two instructions:
 *     a short conditional branch to branch around a large unconditional
 *     branch. Thus, we can handle branch offsets of imm24 instead of just imm20.
 */

void                emitter::emitIns_J(instruction   ins,
                                       BasicBlock *  dst,
                                       int           instrCount /* = 0 */)
{
    insFormat  fmt = IF_NONE;

    if (dst != NULL)
    {
        assert(dst->bbFlags & BBF_JMP_TARGET);
    }
    else
    {
        assert(instrCount != 0);
    }

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_b:
        fmt = IF_T2_J2;     /* Assume the jump will be long */
        break;

    case INS_beq:
    case INS_bne:
    case INS_bhs:
    case INS_blo:
    case INS_bmi:
    case INS_bpl:
    case INS_bvs:
    case INS_bvc:
    case INS_bhi:
    case INS_bls:
    case INS_bge:
    case INS_blt:
    case INS_bgt:
    case INS_ble:
        fmt = IF_LARGEJMP;  /* Assume the jump will be long */
        break;
    }
    assert((fmt == IF_LARGEJMP) ||
           (fmt == IF_T2_J2));

    instrDescJmp  * id  = emitNewInstrJmp();
    insSize         isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idInsFmt(fmt);
    id->idInsSize(isz);

#ifdef DEBUG
    // Mark the finally call
    if (ins == INS_b && emitComp->compCurBB->bbJumpKind == BBJ_CALLFINALLY)
    {
        id->idDebugOnlyInfo()->idFinallyCall = true;
    }
#endif // DEBUG

    /* Assume the jump will be long */

    id->idjShort = 0;
    if (dst != NULL)
    {
        id->idAddr()->iiaBBlabel = dst;
        id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst);

#ifdef DEBUG
        if (emitComp->opts.compLargeBranches)        // Force long branches
            id->idjKeepLong = 1;
#endif // DEBUG
    }
    else
    {
        id->idAddr()->iiaSetInstrCount(instrCount);
        id->idjKeepLong = false;
        /* This jump must be short */
        emitSetShortJump(id);
        id->idSetIsBound();
    }

    /* Record the jump's IG and offset within it */

    id->idjIG                 = emitCurIG;
    id->idjOffs               = emitCurIGsize;

    /* Append this jump to this IG's jump list */

    id->idjNext               = emitCurIGjmpList;
                                emitCurIGjmpList = id;

#if EMITTER_STATS
    emitTotalIGjmps++;
#endif

    /* Figure out the max. size of the jump/call instruction */

    if  (!id->idjKeepLong)
    {
        insGroup    *   tgt = NULL;

        /* Can we guess at the jump distance? */

        if (dst != NULL)
        {
            tgt = (insGroup*)emitCodeGetCookie(dst);
        }

        if  (tgt)
        {
            UNATIVE_OFFSET  srcOffs;
            int             jmpDist;

            assert(JMP_SIZE_SMALL == JCC_SIZE_SMALL);

            /* This is a backward jump - figure out the distance */

            srcOffs = emitCurCodeOffset + emitCurIGsize;

            /* Compute the distance estimate */

            jmpDist = srcOffs - tgt->igOffs; assert(jmpDist >= 0);
            jmpDist += 4;  // Adjustment for ARM PC

            switch (fmt)
            {
            case IF_T2_J2:
                if  (JMP_DIST_SMALL_MAX_NEG <= -jmpDist)
                {
                    /* This jump surely will be short */
                    emitSetShortJump(id);
                }
                break;

            case IF_LARGEJMP:
                if  (JCC_DIST_SMALL_MAX_NEG <= -jmpDist)
                {
                    /* This jump surely will be short */
                    emitSetShortJump(id);
                }
                else if  (JCC_DIST_MEDIUM_MAX_NEG <= -jmpDist)
                {
                    /* This jump surely will be medium */
                    emitSetMediumJump(id);
                }
                break;

            default:
                unreached();
                break;
            }
        }
    }

#ifdef ARM_HAZARD_AVOIDANCE
    id->idKraitNop(emitKraitHazardActive(id));
#endif

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add a label instruction.
 */

void                emitter::emitIns_R_L  (instruction   ins,
                                           emitAttr      attr,
                                           BasicBlock *  dst,
                                           regNumber     reg)
{
    insFormat  fmt      = IF_NONE;

    assert(dst->bbFlags & BBF_JMP_TARGET);

    /* Figure out the encoding format of the instruction */
    switch (ins)
    {
    case INS_movt:
    case INS_movw:
        fmt = IF_T2_N1;
        break;
    }
    assert(fmt == IF_T2_N1);

    instrDescJmp  * id  = emitNewInstrJmp();
    insSize         isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idReg1(reg);
    id->idInsFmt(fmt);
    id->idInsSize(isz);

#ifdef DEBUG
    // Mark the catch return
    if (emitComp->compCurBB->bbJumpKind == BBJ_EHCATCHRET)
    {
        id->idDebugOnlyInfo()->idCatchRet = true;
    }
#endif // DEBUG

    id->idAddr()->iiaBBlabel = dst;
    id->idjShort = false;
    id->idjKeepLong = true;

    /* Record the jump's IG and offset within it */

    id->idjIG                 = emitCurIG;
    id->idjOffs               = emitCurIGsize;

    /* Append this jump to this IG's jump list */

    id->idjNext               = emitCurIGjmpList;
                                emitCurIGjmpList = id;
    
    // Set the relocation flags - these give hint to zap to perform
    // relocation of the specified 32bit address.
    id->idSetRelocFlags(attr);

#if EMITTER_STATS
    emitTotalIGjmps++;
#endif

    dispIns(id);
    appendToCurIG(id);
}


/*****************************************************************************
 *
 *  Add a data label instruction.
 */

void                emitter::emitIns_R_D  (instruction   ins,
                                           emitAttr      attr,
                                           unsigned      offs,
                                           regNumber     reg)
{
    noway_assert((ins == INS_movw) || (ins == INS_movt));

    insFormat   fmt      = IF_T2_N2;
    instrDesc * id  = emitNewInstrSC(attr, offs);
    insSize     isz = emitInsSize(fmt);

    id->idIns(ins);
    id->idReg1(reg);
    id->idInsFmt(fmt);
    id->idInsSize(isz);

#if RELOC_SUPPORT
    if (emitComp->opts.compReloc)
    {
        // Set the relocation flags - these give hint to zap to perform
        // relocation of the specified 32bit address.
        id->idSetRelocFlags(attr);
    }
#endif // RELOC_SUPPORT

    dispIns(id);
    appendToCurIG(id);
}

void                emitter::emitIns_J_R   (instruction   ins,
                                            emitAttr      attr,
                                            BasicBlock *  dst,
                                            regNumber     reg)
{
    assert(dst->bbFlags & BBF_JMP_TARGET);

    instrDescJmp *id;
    if (ins == INS_adr) {
        id = emitNewInstrLbl();

        id->idIns(INS_adr);
        id->idInsFmt(IF_T2_M1);
        id->idInsSize(emitInsSize(IF_T2_M1));
        id->idAddr()->iiaBBlabel  = dst;
        id->idReg1(reg);
        id->idReg2(REG_PC);

        /* Assume the label reference will be long */

        id->idjShort              = 0;
        id->idjKeepLong           = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst);
    }
    else {
        assert(ins == INS_cbz || INS_cbnz);
        assert(isLowRegister(reg));
        id  = emitNewInstrJmp();

        id->idIns(ins);
        id->idInsFmt(IF_T1_I);
        id->idInsSize(emitInsSize(IF_T1_I));
        id->idReg1(reg);

        /* This jump better be short or-else! */
        id->idjShort             = true;
        id->idAddr()->iiaBBlabel = dst;
        id->idjKeepLong          = false;
    }
    
    /* Record the jump's IG and offset within it */

    id->idjIG                 = emitCurIG;
    id->idjOffs               = emitCurIGsize;

    /* Append this jump to this IG's jump list */

    id->idjNext               = emitCurIGjmpList;
                                emitCurIGjmpList = id;

#if EMITTER_STATS
    emitTotalIGjmps++;
#endif

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Add a call instruction (direct or indirect).
 *      argSize<0 means that the caller will pop the arguments
 *
 * The other arguments are interpreted depending on callType as shown:
 * Unless otherwise specified, ireg,xreg,xmul,disp should have default values.
 *
 * EC_FUNC_TOKEN       : addr is the method address
 * EC_FUNC_ADDR        : addr is the absolute address of the function
 *
 * If callType is one of these emitCallTypes, addr has to be NULL.
 * EC_INDIR_R          : "call ireg".
 *
 * For ARM xreg, xmul and disp are never used and should always be 0/REG_NA.
 *
 *  Please consult the "debugger team notification" comment in genFnProlog().
 */

void                emitter::emitIns_Call(EmitCallType  callType,
                                          CORINFO_METHOD_HANDLE methHnd,                       // used for pretty printing
                                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)     // used to report call sites to the EE
                                          void*         addr,
                                          ssize_t       argSize,
                                          emitAttr      retSize,
                                          VARSET_VALARG_TP ptrVars,
                                          regMaskTP     gcrefRegs,
                                          regMaskTP     byrefRegs,
                                          IL_OFFSETX    ilOffset   /* = BAD_IL_OFFSET */,
                                          regNumber     ireg    /* = REG_NA */,
                                          regNumber     xreg    /* = REG_NA */,
                                          unsigned      xmul    /* = 0     */,
                                          int           disp    /* = 0     */,
                                          bool          isJump  /* = false */,
                                          bool          isNoGC  /* = false */,
                                          bool          isProfLeaveCB /* = false */)
{
    /* Sanity check the arguments depending on callType */

    assert(callType < EC_COUNT);
    assert((callType != EC_FUNC_TOKEN && callType != EC_FUNC_ADDR) ||
           (ireg == REG_NA && xreg == REG_NA && xmul == 0 && disp == 0));
    assert(callType < EC_INDIR_R || addr == NULL);
    assert(callType != EC_INDIR_R ||
           (ireg < REG_COUNT && xreg == REG_NA && xmul == 0 && disp == 0));

    // ARM never uses these
    assert(xreg == REG_NA && xmul == 0 && disp == 0);

    // Our stack level should be always greater than the bytes of arguments we push. Just
    // a sanity test.
    assert((unsigned) abs(argSize) <= codeGen->genStackLevel);

    int             argCnt;
    instrDesc      *id;

    /* This is the saved set of registers after a normal call */
    regMaskTP savedSet = RBM_CALLEE_SAVED;

    /* some special helper calls have a different saved set registers */

    if (isNoGC)
    {
        assert(emitNoGChelper(Compiler::eeGetHelperNum(methHnd)));

        // This call will preserve the liveness of most registers
        //
        // - On the ARM the NOGC helpers will preserve all registers,
        //   except for those listed in the RBM_CALLEE_TRASH_NOGC mask

        savedSet = RBM_ALLINT & ~RBM_CALLEE_TRASH_NOGC;

        // In case of Leave profiler callback, we need to preserve liveness of REG_PROFILER_RET_SCRATCH
        if (isProfLeaveCB)
        {
            savedSet |= RBM_PROFILER_RET_SCRATCH;
        }
    }
    else
    {
        assert(!emitNoGChelper(Compiler::eeGetHelperNum(methHnd)));
    }

    /* Trim out any callee-trashed registers from the live set */

    gcrefRegs &= savedSet;
    byrefRegs &= savedSet;

#ifdef  DEBUG
    if  (EMIT_GC_VERBOSE)
    {
        printf("Call: GCvars=%s ", VarSetOps::ToString(emitComp, ptrVars));
        dumpConvertedVarSet(emitComp, ptrVars);
        printf(", gcrefRegs=");
        printRegMaskInt(gcrefRegs);
        emitDispRegSet (gcrefRegs);
        printf(", byrefRegs=");
        printRegMaskInt(byrefRegs);
        emitDispRegSet (byrefRegs);
        printf("\n");
    }
#endif

    assert(  argSize % (int)sizeof(void*) == 0);
    argCnt = argSize / (int)sizeof(void*);
    
#ifdef DEBUGGING_SUPPORT
    /* Managed RetVal: emit sequence point for the call */
    if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET)
    {
        codeGen->genIPmappingAdd(ilOffset, false);
    }
#endif

    /*
        We need to allocate the appropriate instruction descriptor based
        on whether this is a direct/indirect call, and whether we need to
        record an updated set of live GC variables.

        The stats for a ton of classes is as follows:

            Direct call w/o  GC vars        220,216
            Indir. call w/o  GC vars        144,781

            Direct call with GC vars          9,440
            Indir. call with GC vars          5,768
     */

    if  (callType >= EC_INDIR_R)
    {
        /* Indirect call, virtual calls */

        assert(callType == EC_INDIR_R);

        id  = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize);
    }
    else
    {
        /* Helper/static/nonvirtual/function calls (direct or through handle),
           and calls to an absolute addr. */

        assert(callType == EC_FUNC_TOKEN ||
               callType == EC_FUNC_ADDR);

        id  = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize);
    }

    /* Update the emitter's live GC ref sets */

    VarSetOps::Assign(emitComp, emitThisGCrefVars, ptrVars);
    emitThisGCrefRegs = gcrefRegs;
    emitThisByrefRegs = byrefRegs;

    /* Set the instruction - special case jumping a function */
    instruction ins;
    insFormat   fmt = IF_NONE;

    id->idSetIsNoGC(isNoGC);

    /* Record the address: method, indirection, or funcptr */

    if  (callType > EC_FUNC_ADDR)
    {
        /* This is an indirect call (either a virtual call or func ptr call) */

        switch (callType)
        {
        case EC_INDIR_R:            // the address is in a register

            id->idSetIsCallRegPtr();

            if (isJump)
            {
                ins = INS_bx;      // INS_bx  Reg
            }
            else
            {
                ins = INS_blx;     // INS_blx Reg
            }
            fmt = IF_T1_D2;

            id->idIns(ins);
            id->idInsFmt(fmt); 
            id->idInsSize(emitInsSize(fmt));
            id->idReg3(ireg);
            assert(xreg == REG_NA);
            break;

        default:
            NO_WAY("unexpected instruction");
            break;
        }

    }
    else
    {
        /* This is a simple direct call: "call helper/method/addr" */

        assert(callType == EC_FUNC_TOKEN || callType == EC_FUNC_ADDR);

        assert(addr != NULL);
        assert(codeGen->validImmForBL((ssize_t)addr));

        if (isJump)
        {
            ins = INS_b;      // INS_b imm24
        }
        else
        {
            ins = INS_bl;     // INS_bl imm24
        }

        fmt = IF_T2_J3;

        id->idIns(ins);
        id->idInsFmt(fmt); 
        id->idInsSize(emitInsSize(fmt));

        id->idAddr()->iiaAddr = (BYTE*)addr;

        if (callType == EC_FUNC_ADDR)
        {
            id->idSetIsCallAddr();
        }

#if RELOC_SUPPORT
        if (emitComp->opts.compReloc)
        {
            // Since this is an indirect call through a pointer and we don't
            // currently pass in emitAttr into this function we have decided
            // to always mark the displacement as being relocatable.

            id->idSetIsDspReloc();
        }
#endif

#ifdef ARM_HAZARD_AVOIDANCE
        // Unconditional calls/branches may need nop.w for Krait errata
        id->idKraitNop(emitKraitHazardActive(id));
#endif
    }

#ifdef  DEBUG
    if  (EMIT_GC_VERBOSE)
    {
        if  (id->idIsLargeCall())
        {
            printf("[%02u] Rec call GC vars = %s\n", id->idDebugOnlyInfo()->idNum, VarSetOps::ToString(emitComp, ((instrDescCGCA*)id)->idcGCvars));
        }
    }
#endif

#if defined(DEBUG) || defined(LATE_DISASM)
    id->idDebugOnlyInfo()->idMemCookie = (size_t) methHnd;    // method token
    id->idDebugOnlyInfo()->idClsCookie = 0;
    id->idDebugOnlyInfo()->idCallSig   = sigInfo;
#endif

#if defined(LATE_DISASM)
    if (addr != nullptr)
    {
        codeGen->getDisAssembler().disSetMethod((size_t)addr, methHnd);
    }
#endif // defined(LATE_DISASM)

    dispIns(id);
    appendToCurIG(id);
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register (any-reg) to be used in 
 *   a Thumb-1 encoding in the M4 position 
 */

inline unsigned     insEncodeRegT1_M4(regNumber reg)
{
    assert(reg < REG_STK);

    return reg << 3;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register (any-reg) to be used in 
 *   a Thumb-1 encoding in the D4 position 
 */

inline unsigned     insEncodeRegT1_D4(regNumber reg)
{
    assert(reg < REG_STK);

    return (reg & 0x7) | ((reg & 0x8) << 4);
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register (low-only) to be used in 
 *   a Thumb-1 encoding in the M3 position 
 */

inline unsigned     insEncodeRegT1_M3(regNumber reg)
{
    assert(reg < REG_R8);

    return reg << 6;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register (low-only) to be used in 
 *   a Thumb-1 encoding in the N3 position 
 */

inline unsigned     insEncodeRegT1_N3(regNumber reg)
{
    assert(reg < REG_R8);

    return reg << 3;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register (low-only) to be used in 
 *   a Thumb-1 encoding in the D3 position 
 */

inline unsigned     insEncodeRegT1_D3(regNumber reg)
{
    assert(reg < REG_R8);

    return reg;
}
/*****************************************************************************
 *
 *  Returns an encoding for the specified register (low-only) to be used in 
 *   a Thumb-1 encoding in the DI position 
 */

inline unsigned     insEncodeRegT1_DI(regNumber reg)
{
    assert(reg < REG_R8);

    return reg << 8;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register to be used in 
 *   a Thumb-2 encoding in the N position 
 */

inline unsigned     insEncodeRegT2_N(regNumber reg)
{
    assert(reg < REG_STK);

    return reg << 16;
}

inline unsigned floatRegIndex(regNumber reg, int size)
{
    // theoretically this could support quad floats as well but for now...
    assert(size == EA_8BYTE || size == EA_4BYTE);

    if (size == EA_8BYTE)
        assert(emitter::isDoubleReg(reg));
    else
        assert(emitter::isFloatReg(reg));

    unsigned result = reg - REG_F0;
    
    // the assumption here is that the register F8 also refers to D4
    if (size == EA_8BYTE)
    {
        result >>= 1;
    }

    return result;
}

// variant: SOME arm VFP instructions use the convention that 
// for doubles, the split bit holds the msb of the register index
// for singles it holds the lsb
// excerpt : d = if dp_operation then UInt(D:Vd) 
// if single  UInt(Vd:D);

inline unsigned floatRegEncoding(unsigned index, int size, bool variant=false)
{
    if (!variant || size == EA_8BYTE)
        return index;
    else 
    {
        return ((index&1 ) << 4) | (index >> 1);
    }
}

// thumb2 VFP M register encoding
inline unsigned insEncodeRegT2_VectorM(regNumber reg, int size, bool variant)
{
    unsigned enc = floatRegIndex(reg, size);
    enc = floatRegEncoding(enc, size, variant);
    return ((enc & 0xf) << 0) | ((enc & 0x10) << 1);
}

// thumb2 VFP N register encoding
inline unsigned insEncodeRegT2_VectorN(regNumber reg, int size, bool variant)
{
    unsigned enc = floatRegIndex(reg, size);
    enc = floatRegEncoding(enc, size, variant);
    return ((enc & 0xf) << 16) | ((enc & 0x10) << 3);
}

// thumb2 VFP D register encoding
inline unsigned insEncodeRegT2_VectorD(regNumber reg, int size, bool variant)
{
    unsigned enc = floatRegIndex(reg, size);
    enc = floatRegEncoding(enc, size, variant);
    return ((enc & 0xf) << 12) | ((enc & 0x10) << 18);
}


/*****************************************************************************
 *
 *  Returns an encoding for the specified register to be used in 
 *   a Thumb-2 encoding in the T position 
 */

inline unsigned     insEncodeRegT2_T(regNumber reg)
{
    assert(reg < REG_STK);

    return reg << 12;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register to be used in 
 *   a Thumb-2 encoding in the D position 
 */

inline unsigned     insEncodeRegT2_D(regNumber reg)
{
    assert(reg < REG_STK);

    return reg <<  8;
}

/*****************************************************************************
 *
 *  Returns an encoding for the specified register to be used in 
 *   a Thumb-2 encoding in the M position 
 */

inline unsigned     insEncodeRegT2_M(regNumber reg)
{
    assert(reg < REG_STK);

    return reg;
}

/*****************************************************************************
 *
 *  Returns the encoding for the Set Flags bit to be used in a Thumb-2 encoding
 */

unsigned     emitter::insEncodeSetFlags(insFlags sf)
{
    if (sf == INS_FLAGS_SET)
        return (1 << 20);
    else
        return 0;
}

/*****************************************************************************
 *
 *  Returns the encoding for the Shift Type bits to be used in a Thumb-2 encoding
 */

unsigned     emitter::insEncodeShiftOpts(insOpts opt)
{
    if (opt == INS_OPTS_NONE)
        return 0;
    else if (opt == INS_OPTS_LSL)
        return 0x00;
    else if (opt == INS_OPTS_LSR)
        return 0x10;
    else if (opt == INS_OPTS_ASR)
        return 0x20;
    else if (opt == INS_OPTS_ROR)
        return 0x30;
    else if (opt == INS_OPTS_RRX)
        return 0x30;

    assert(!"Invalid insOpts");
    return 0;
} 

/*****************************************************************************
 *
 *  Returns the encoding for the PUW bits to be used in a T2_G0 Thumb-2 encoding
 */

unsigned     emitter::insEncodePUW_G0(insOpts  opt, int imm)
{
    unsigned result = 0;

    if (opt != INS_OPTS_LDST_POST_INC)
        result |= (1 << 24);               // The P bit

    if (imm >= 0)
        result |= (1 << 23);               // The U bit
    
    if (opt != INS_OPTS_NONE)
        result |= (1 << 21);               // The W bits
    return result;
} 

/*****************************************************************************
 *
 *  Returns the encoding for the PUW bits to be used in a T2_H0 Thumb-2 encoding
 */

unsigned     emitter::insEncodePUW_H0(insOpts  opt, int imm)
{
    unsigned result = 0;

    if (opt != INS_OPTS_LDST_POST_INC)
        result |= (1 << 10);               // The P bit

    if (imm >= 0)
        result |= (1 << 9);                // The U bit
    
    if (opt != INS_OPTS_NONE)
        result |= (1 << 8);                // The W bits

    return result;
} 

/*****************************************************************************
 *
 *  Returns the encoding for the Shift Count bits to be used in a Thumb-2 encoding
 */

inline unsigned     insEncodeShiftCount(int imm)
{
    unsigned result;

    assert((imm & 0x001F) == imm);
    result  = (imm & 0x03) <<  6;
    result |= (imm & 0x1C) << 10;

    return result;
}

/*****************************************************************************
 *
 *  Returns the encoding for the immediate use by BFI/BFC Thumb-2 encodings
 */

inline unsigned     insEncodeBitFieldImm(int imm)
{
    unsigned result;

    assert((imm & 0x03FF) == imm);
    result  = (imm & 0x001f);
    result |= (imm & 0x0060) << 1;
    result |= (imm & 0x0380) << 5;

    return result;
}

/*****************************************************************************
 *
 *  Unscales the immediate based on the operand size in 'size'
 */
/*static*/ int          emitter::insUnscaleImm(int imm, emitAttr  size)
{    
    switch (size)
    {
    case EA_8BYTE:
    case EA_4BYTE:
        assert((imm & 0x0003) == 0);
        imm >>= 2;
        break;

    case EA_2BYTE:
        assert((imm & 0x0001) == 0);
        imm >>= 1;
        break;

    case EA_1BYTE:
        // Do nothing
        break;

    default:
        assert(!"Invalid value in size");
        break;
    }
    return imm;
}

/*****************************************************************************
 *
 *  Emit a Thumb-1 instruction (a 16-bit integer as code)
 */

/*static*/ unsigned   emitter::emitOutput_Thumb1Instr(BYTE *dst, ssize_t code)
{
    unsigned short word1 = code & 0xffff;
    assert(word1 == code);

#ifdef DEBUG
    unsigned short top5bits = (word1 & 0xf800) >> 11;
    assert(top5bits < 29); 
#endif

    MISALIGNED_WR_I2(dst, word1);

    return  sizeof(short);
}
/*****************************************************************************
 *
 *  Emit a Thumb-2 instruction (two 16-bit integers as code)
 */

/*static*/ unsigned   emitter::emitOutput_Thumb2Instr(BYTE *dst, ssize_t code)
{
    unsigned short word1 = (code >> 16) & 0xffff;
    unsigned short word2 = (code      ) & 0xffff;
    assert(((word1 << 16) | word2) == code);

#ifdef DEBUG
    unsigned short top5bits = (word1 & 0xf800) >> 11;
    assert(top5bits >= 29); 
#endif

    MISALIGNED_WR_I2(dst, word1);
    dst += 2;
    MISALIGNED_WR_I2(dst, word2);

    return  sizeof(short) * 2;
}




/*****************************************************************************
 *
 *  Output a local jump instruction.
 *  Note that this may be invoked to overwrite an existing jump instruction at 'dst'
 *  to handle forward branch patching.
 */

BYTE    *           emitter::emitOutputLJ(insGroup  *ig, BYTE *dst, instrDesc *i)
{
    unsigned        srcOffs;
    unsigned        dstOffs;
    ssize_t         distVal;

    instrDescJmp *  id   = (instrDescJmp*)i;
    instruction     ins  = id->idIns();
    ssize_t         code;

    bool            loadLabel = false;
    bool            isJump    = false;
    bool            relAddr   = true; // does the instruction use relative-addressing?

    size_t          sdistneg;

    switch (ins)
    {
    default:
        sdistneg = JCC_DIST_SMALL_MAX_NEG;
        isJump = true;
        break;

    case INS_cbz:
    case INS_cbnz:
        // One size fits all!
        sdistneg = 0;
        isJump = true;
        break;

    case INS_adr:
        sdistneg = LBL_DIST_SMALL_MAX_NEG;
        loadLabel = true;
        break;

    case INS_movw:
    case INS_movt:
        sdistneg = LBL_DIST_SMALL_MAX_NEG;
        relAddr = false;
        loadLabel = true;
        break;
    }

    /* Figure out the distance to the target */

    srcOffs = emitCurCodeOffs(dst);
    if (id->idAddr()->iiaHasInstrCount())
    {
        assert(ig != NULL);
        int      instrCount = id->idAddr()->iiaGetInstrCount();
        unsigned insNum     = emitFindInsNum(ig, id);
        if (instrCount < 0)
        {
            // Backward branches using instruction count must be within the same instruction group.
            assert(insNum + 1 >= (unsigned)(-instrCount));
        }
        dstOffs = ig->igOffs + emitFindOffset(ig, (insNum + 1 + instrCount));
    }
    else
    {
        dstOffs = id->idAddr()->iiaIGlabel->igOffs;
    }

    if (relAddr)
    {
        if (ins == INS_adr)
        {
            // for adr, the distance is calculated from 4-byte aligned srcOffs.
            distVal = (ssize_t) ((emitOffsetToPtr(dstOffs) - (BYTE*)(((size_t)emitOffsetToPtr(srcOffs))&~3)) + 1);
        }
        else
        {
            distVal = (ssize_t) (emitOffsetToPtr(dstOffs) - emitOffsetToPtr(srcOffs));            
        }
    }
    else
    {    
        assert (ins == INS_movw || ins == INS_movt);
        distVal = (ssize_t) emitOffsetToPtr(dstOffs) + 1; // Or in thumb bit
    }

    if  (dstOffs <= srcOffs)
    {
        /* This is a backward jump - distance is known at this point */

#if     DEBUG_EMIT
        if  (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
        {
            size_t      blkOffs = id->idjIG->igOffs;

            if  (INTERESTING_JUMP_NUM == 0)
            printf("[3] Jump %u:\n", id->idDebugOnlyInfo()->idNum);
            printf("[3] Jump  block is at %08X - %02X = %08X\n", blkOffs, emitOffsAdj, blkOffs - emitOffsAdj);
            printf("[3] Jump        is at %08X - %02X = %08X\n", srcOffs, emitOffsAdj, srcOffs - emitOffsAdj);
            printf("[3] Label block is at %08X - %02X = %08X\n", dstOffs, emitOffsAdj, dstOffs - emitOffsAdj);
        }
#endif

        // This format only supports forward branches
        noway_assert(id->idInsFmt() != IF_T1_I); 

        /* Can we use a short jump? */

        if  (isJump && ((unsigned)(distVal - 4) >= (unsigned)sdistneg))
        {
            emitSetShortJump(id);
        }
    }
    else
    {
        /* This is a  forward jump - distance will be an upper limit */

        emitFwdJumps  = true;

        /* The target offset will be closer by at least 'emitOffsAdj', but only if this
           jump doesn't cross the hot-cold boundary. */

        if (!emitJumpCrossHotColdBoundary(srcOffs, dstOffs))
        {
            dstOffs -= emitOffsAdj;
            distVal -= emitOffsAdj;
        }

        /* Record the location of the jump for later patching */

        id->idjOffs = dstOffs;

        /* Are we overflowing the id->idjOffs bitfield? */
        if (id->idjOffs != dstOffs)
            IMPL_LIMITATION("Method is too large");       

#if     DEBUG_EMIT
        if  (id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
        {
            size_t      blkOffs = id->idjIG->igOffs;

            if  (INTERESTING_JUMP_NUM == 0)
            printf("[4] Jump %u:\n", id->idDebugOnlyInfo()->idNum);
            printf("[4] Jump  block is at %08X\n"              , blkOffs);
            printf("[4] Jump        is at %08X\n"              , srcOffs);
            printf("[4] Label block is at %08X - %02X = %08X\n", dstOffs + emitOffsAdj, emitOffsAdj, dstOffs);
        }
#endif

    }

    /* Adjust the offset to emit relative to the end of the instruction */

    if (relAddr)
        distVal -= 4;

#ifdef  DEBUG
    if (0 && emitComp->verbose)
    {
        size_t  sz          = 4; // Thumb-2 pretends all instructions are 4-bytes long for computing jump offsets?
        int     distValSize = id->idjShort ? 4 : 8;
        printf("; %s jump [%08X/%03u] from %0*X to %0*X: dist = %08XH\n",
            (dstOffs <= srcOffs)?"Fwd":"Bwd", dspPtr(id), id->idDebugOnlyInfo()->idNum,
            distValSize, srcOffs + sz,
            distValSize, dstOffs,
            distVal);
    }
#endif

    insFormat fmt = id->idInsFmt();

    if (isJump)
    {
        /* What size jump should we use? */

        if  (id->idjShort)
        {
            /* Short jump */

            assert(!id->idjKeepLong);
            assert(emitJumpCrossHotColdBoundary(srcOffs, dstOffs) == false);

            assert(JMP_SIZE_SMALL == JCC_SIZE_SMALL);
            assert(JMP_SIZE_SMALL == 2);

            /* For forward jumps, record the address of the distance value */
            id->idjTemp.idjAddr = (distVal > 0) ? dst : NULL;

            dst = emitOutputShortBranch(dst, ins, fmt, distVal, id);
        }
        else
        {
            /* Long  jump */

            /* For forward jumps, record the address of the distance value */
            id->idjTemp.idjAddr = (dstOffs > srcOffs) ? dst : NULL;

#ifdef ARM_HAZARD_AVOIDANCE
            if (id->idKraitNop())
            {
                // This is a pseudo-format representing a 32-bit nop followed by unconditional branch.
                // First emit the nop

                dst = emitOutputNOP(dst, INS_nopw, IF_T2_A);

                // The distVal was computed based on the beginning of the pseudo-instruction, which is
                // the 32-bit nop. So subtract the size of the nop from the offset, so it is relative to the
                // unconditional branch.
                distVal -= 4;
            }
#endif

            if (fmt == IF_LARGEJMP)
            {
                // This is a pseudo-instruction format representing a large conditional branch, to allow
                // us to get a greater branch target range than we can get by using a straightforward conditional
                // branch. It is encoded as a short conditional branch that branches around a long unconditional
                // branch.
                //
                // Conceptually, we have:
                //
                //      b<cond> L_target
                //
                // The code we emit is:
                //
                //      b<!cond> L_not  // 2 bytes. Note that we reverse the condition.
                //      b L_target      // 4 bytes
                //   L_not:
                //
                // Note that we don't actually insert any blocks: we simply encode "b <!cond> L_not" as a branch with
                // the correct offset. Note also that this works for both integer and floating-point conditions, because
                // the condition inversion takes ordered/unordered into account, preserving NaN behavior. For example,
                // "GT" (greater than) is inverted to "LE" (less than, equal, or unordered).
                //
                // History: previously, we generated:
                //      it<cond>
                //      b L_target
                // but the "it" instruction was deprecated, so we can't use it.

                dst = emitOutputShortBranch(dst,
                                            emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(ins))), // reverse the conditional instruction
                                            IF_T1_K,
                                            6 - 4, /* 6 bytes from start of this large conditional pseudo-instruction to L_not. Jumps are encoded as offset from instr address + 4. */
                                            NULL /* only used for cbz/cbnz */);

                // Now, pretend we've got a normal unconditional branch, and fall through to the code to emit that.
                ins = INS_b;
                fmt = IF_T2_J2;

                // The distVal was computed based on the beginning of the pseudo-instruction, which is
                // the IT. So subtract the size of the IT from the offset, so it is relative to the
                // unconditional branch.
                distVal -= 2;
            }

            code = emitInsCode(ins, fmt);

            if (fmt == IF_T2_J1)
            {
                // Can't use this form for jumps between the hot and cold regions
                assert(!id->idjKeepLong);
                assert(emitJumpCrossHotColdBoundary(srcOffs, dstOffs) == false);

                assert((distVal & 1) == 0);
                assert(distVal >= -1048576);
                assert(distVal <=  1048574);

                if (distVal < 0)
                    code |= 1<<26;
                code |=  ((distVal >> 1) & 0x0007ff);
                code |= (((distVal >> 1) & 0x01f800) << 5);
                code |= (((distVal >> 1) & 0x020000) >> 4);
                code |= (((distVal >> 1) & 0x040000) >> 7);
            }
            else if (fmt == IF_T2_J2)
            {
                assert((distVal & 1) == 0);
#ifdef RELOC_SUPPORT
                if (emitComp->opts.compReloc && emitJumpCrossHotColdBoundary(srcOffs, dstOffs))
                {
                    // dst isn't an actual final target location, just some intermediate
                    // location.  Thus we cannot make any guarantees about distVal (not
                    // even the direction/sign).  Instead we don't encode any offset and
                    // rely on the relocation to do all the work
                }
                else
#endif
                {
                    assert(distVal >= -16777216);
                    assert(distVal <=  16777214);

                    if (distVal < 0)
                        code |= 1<<26;
                    code |=  ((distVal >> 1) & 0x0007ff);
                    code |= (((distVal >> 1) & 0x1ff800) << 5);

                    bool S    = (distVal < 0);
                    bool I1   = ((distVal & 0x00800000) == 0);
                    bool I2   = ((distVal & 0x00400000) == 0);

                    if (S ^ I1)
                        code |= (1 << 13);   // J1 bit
                    if (S ^ I2)
                        code |= (1 << 11);   // J2 bit
                }
            }
            else
            {
                assert(!"Unknown fmt");
            }

            unsigned instrSize = emitOutput_Thumb2Instr(dst, code);

#ifdef RELOC_SUPPORT
            if (emitComp->opts.compReloc)
            {
                if (emitJumpCrossHotColdBoundary(srcOffs, dstOffs)) 
                {
                    assert(id->idjKeepLong);
                    if (emitComp->info.compMatchedVM)
                    {
                        void* target = emitOffsetToPtr(dstOffs);
                        emitRecordRelocation((void*)dst, target, IMAGE_REL_BASED_THUMB_BRANCH24);
                    }
                }
            }
#endif // RELOC_SUPPORT

            dst += instrSize;
        }
    }
    else if (loadLabel)
    {
        /* For forward jumps, record the address of the distance value */
        id->idjTemp.idjAddr = (distVal > 0) ? dst : NULL;

        code = emitInsCode(ins, fmt);

        if (fmt == IF_T1_J3)
        {
            assert((dstOffs & 3) == 0);     // The target label must be 4-byte aligned 
            assert(distVal >= 0);
            assert(distVal <= 1022);
            code |= ((distVal >> 2) & 0xff);
            
            dst  += emitOutput_Thumb1Instr(dst, code);
        }
        else if (fmt == IF_T2_M1)
        {
            assert(distVal >= -4095);
            assert(distVal <= +4095);
            if (distVal < 0)
            {
                code |= 0x00A0<<16;
                distVal = -distVal;
            }
            assert((distVal & 0x0fff) == distVal);
            code |=  (distVal & 0x00ff);
            code |= ((distVal & 0x0700) << 4);

            code |= ((distVal & 0x0800) << 15);
            code |= id->idReg1() << 8; 
            
            dst  += emitOutput_Thumb2Instr(dst, code);
        }
        else if (fmt == IF_T2_N1)
        {
            code |= insEncodeRegT2_D(id->idReg1());
            unsigned imm = distVal;
            if (ins == INS_movw)
            {
                imm &= 0xffff;
            }
            else
            {
                imm = (imm >> 16) & 0xffff;
            }
            ((instrDescJmp*)id)->idjTemp.idjAddr = (dstOffs > srcOffs) ? dst : NULL;

            assert((imm & 0x0000ffff) == imm);
            code |=  (imm & 0x00ff);
            code |= ((imm & 0x0700) <<  4);
            code |= ((imm & 0x0800) << 15);
            code |= ((imm & 0xf000) <<  4);
            dst  += emitOutput_Thumb2Instr(dst, code);

            if (id->idIsCnsReloc() || id->idIsDspReloc())
            {
                assert(ins == INS_movt || ins == INS_movw);
                if ((ins == INS_movt) && emitComp->info.compMatchedVM)
                    emitRecordRelocation((void*)(dst-8), (void *)distVal, IMAGE_REL_BASED_THUMB_MOV32);
            }
        }
        else
        {
            assert(!"Unknown fmt");
        }
    }

    return  dst;
}


/*****************************************************************************
 *
 *  Output a short branch instruction.
 */

BYTE    *           emitter::emitOutputShortBranch(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, instrDescJmp* id)
{
    size_t code;

    code = emitInsCode(ins, fmt);

    if (fmt == IF_T1_K)
    {
        assert((distVal & 1) == 0);
        assert(distVal >= -256);
        assert(distVal <=  254);

        if (distVal < 0)
            code |= 1<<7;
        code |= ((distVal >> 1) & 0x7f);
    }
    else if (fmt == IF_T1_M)
    {
        assert((distVal & 1) == 0);
        assert(distVal >= -2048);
        assert(distVal <=  2046);

        if (distVal < 0)
            code |= 1<<10;
        code |= ((distVal >> 1) & 0x3ff);
    }
    else if (fmt == IF_T1_I)
    {
        assert(id != NULL);
        assert(ins == INS_cbz || INS_cbnz);
        assert((distVal & 1) == 0);
        assert(distVal >= 0);
        assert(distVal <=  126);

        code |= ((distVal << 3) & 0x0200);
        code |= ((distVal << 2) & 0x00F8);
        code |= (  id->idReg1() & 0x0007);
    }
    else
    {
        assert(!"Unknown fmt");
    }

    dst  += emitOutput_Thumb1Instr(dst, code);

    return dst;
}


#ifdef FEATURE_ITINSTRUCTION

/*****************************************************************************
 * The "IT" instruction is deprecated (with a very few exceptions). Don't generate it!
 * Don't delete this code, though, in case we ever want to bring it back.
 *****************************************************************************/

/*****************************************************************************
 *
 *  Output an IT instruction.
 */

BYTE    *           emitter::emitOutputIT(BYTE *dst, instruction ins, insFormat fmt, ssize_t condcode)
{
    ssize_t imm0;
    size_t code, mask, bit;
    
    code  = emitInsCode(ins, fmt);
    code |= (condcode << 4);               // encode firstcond
    imm0  = condcode & 1;                  // this is firstcond[0]
    mask  = code & 0x0f;                   // initialize mask encoded in opcode
    bit   = 0x08;                          // where in mask we are encoding
    while ((mask & (bit-1)) != 0)          // are the remaining bits all zeros?
    {                                      //  then we are done
        // otherwise determine the setting of bit
        if ((imm0 == 1) ^ ((bit & mask) != 0))
        {
            code |= bit;                   // set the current bit
        }
        else
        {
            code &= ~bit;                  // clear the current bit
        }
        bit >>= 1;
    }
    dst += emitOutput_Thumb1Instr(dst, code);

    return dst;
}

#endif // FEATURE_ITINSTRUCTION

/*****************************************************************************
 *
 *  Output a 32-bit nop instruction.
 */

BYTE    *           emitter::emitOutputNOP (BYTE *dst, instruction ins, insFormat fmt)
{   
    size_t code = emitInsCode(ins, fmt);
   
    dst += emitOutput_Thumb2Instr(dst, code);

    return dst;
}

/*****************************************************************************
*
 *  Append the machine code corresponding to the given instruction descriptor
 *  to the code block at '*dp'; the base of the code block is 'bp', and 'ig'
 *  is the instruction group that contains the instruction. Updates '*dp' to
 *  point past the generated code, and returns the size of the instruction
 *  descriptor in bytes.
 */

size_t              emitter::emitOutputInstr(insGroup  *ig,
                                             instrDesc *id, BYTE **dp)
{
    BYTE    *       dst  = *dp;
    BYTE    *       odst = dst;
    size_t          code = 0;
    size_t          sz   = 0;
    instruction     ins  = id->idIns();
    insFormat       fmt  = id->idInsFmt();
    emitAttr        size = id->idOpSize();
    unsigned char   callInstrSize = 0;
    ssize_t         condcode;

#ifdef  DEBUG
    bool            dspOffs = emitComp->opts.dspGCtbls || !emitComp->opts.disDiffable;
#endif // DEBUG

    assert(REG_NA == (int)REG_NA);

    VARSET_TP VARSET_INIT_NOCOPY(GCvars, VarSetOps::UninitVal());

    /* What instruction format have we got? */

    switch (fmt)
    {
        int             imm;
        int             imm0;
        int             mask;
        int             bit;
        BYTE    *       addr;
        regMaskTP       gcrefRegs;
        regMaskTP       byrefRegs;

    case IF_T1_A:    // T1_A    ................                                        
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        dst += emitOutput_Thumb1Instr(dst, code);
        break;

#ifdef FEATURE_ITINSTRUCTION
    case IF_T1_B:    // T1_B    ........cccc....                                           cond                
        assert(id->idGCref() == GCT_NONE);
        condcode = emitGetInsSC(id);
        dst = emitOutputIT(dst, ins, fmt, condcode);
        sz  = SMALL_IDSC_SIZE;
        break;
#endif // FEATURE_ITINSTRUCTION

    case IF_T1_C:    // T1_C    .....iiiiinnnddd                       R1  R2              imm5                            
        sz    = SMALL_IDSC_SIZE;
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_D3(id->idReg1());
        code |= insEncodeRegT1_N3(id->idReg2());
        if (emitInsIsLoadOrStore(ins))
        {
            imm  = insUnscaleImm(imm, size);
        }
        assert((imm & 0x001f) == imm);
        code |= (imm << 6);
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_D0:   // T1_D0   ........Dmmmmddd                       R1* R2*                
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_D4(id->idReg1());
        code |= insEncodeRegT1_M4(id->idReg2());
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_E:    // T1_E    ..........nnnddd                       R1  R2                                          
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_D3(id->idReg1());
        code |= insEncodeRegT1_N3(id->idReg2());
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_F:    // T1_F    .........iiiiiii                       SP                  imm7                
        sz    = emitGetInstrDescSize(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        imm   = insUnscaleImm(imm, size);
        assert((imm & 0x007F) == imm);
        code |= imm;
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_G:    // T1_G    .......iiinnnddd                       R1  R2              imm3                            
        sz    = SMALL_IDSC_SIZE;
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_D3(id->idReg1());
        code |= insEncodeRegT1_N3(id->idReg2());
        assert((imm & 0x0007) == imm);
        code |= (imm << 6);
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_H:    // T1_H    .......mmmnnnddd                       R1  R2  R3                
        sz    = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_D3(id->idReg1());
        code |= insEncodeRegT1_N3(id->idReg2());
        code |= insEncodeRegT1_M3(id->idReg3());
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_I:    // T1_I    ......i.iiiiiddd                       R1                  imm6                
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz  = sizeof(instrDescJmp);
        break;

    case IF_T1_J0:   // T1_J0   .....dddiiiiiiii                       R1                  imm8  
    case IF_T1_J1:   // T1_J1   .....dddiiiiiiii                       R1                  <regmask8>  
    case IF_T1_J2:   // T1_J2   .....dddiiiiiiii                       R1  SP              imm8  
        sz    = emitGetInstrDescSize(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_DI(id->idReg1());
        if (fmt == IF_T1_J2)
        {
            imm = insUnscaleImm(imm, size);
        }
        assert((imm & 0x00ff) == imm);
        code |= imm;
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T1_L0:   // T1_L0   ........iiiiiiii                                           imm8  
    case IF_T1_L1:   // T1_L1   .......Rrrrrrrrr                                           <regmask8> 
        sz    = emitGetInstrDescSize(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        if (fmt == IF_T1_L1)
        {
            assert((imm & 0x3) != 0x3);
            if (imm & 0x3)
                code |= 0x0100;  //  R bit
            imm >>= 2;
        }
        assert((imm & 0x00ff) == imm);
        code |= imm;
        dst  += emitOutput_Thumb1Instr(dst, code);
        break;

    case IF_T2_A:    // T2_A    ................ ................                       
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        dst += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_B:    // T2_B    ................ ............iiii                          imm4                
        sz    = SMALL_IDSC_SIZE;
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        assert((imm & 0x000F) == imm);
        code |= imm;
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_C0:   // T2_C0   ...........Snnnn .iiiddddiishmmmm       R1  R2  R3      S, imm5, sh 
    case IF_T2_C4:   // T2_C4   ...........Snnnn ....dddd....mmmm       R1  R2  R3      S 
    case IF_T2_C5:   // T2_C5   ............nnnn ....dddd....mmmm       R1  R2  R3    
        sz    = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        code |= insEncodeRegT2_N(id->idReg2());
        code |= insEncodeRegT2_M(id->idReg3());
        if (fmt != IF_T2_C5)
            code |= insEncodeSetFlags (id->idInsFlags());
        if (fmt == IF_T2_C0)
        {
            imm   = emitGetInsSC(id);
            code |= insEncodeShiftCount(imm);
            code |= insEncodeShiftOpts(id->idInsOpt());
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_C1:   // T2_C1   ...........S.... .iiiddddiishmmmm       R1  R2          S, imm5, sh   
    case IF_T2_C2:   // T2_C2   ...........S.... .iiiddddii..mmmm       R1  R2          S, imm5 
    case IF_T2_C6:   // T2_C6   ................ ....dddd..iimmmm       R1  R2                   imm2  
        sz    = SMALL_IDSC_SIZE;
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        code |= insEncodeRegT2_M(id->idReg2());
        if (fmt == IF_T2_C6)
        {
            assert((imm & 0x0018) == imm);
            code |= (imm << 1);
        }
        else
        {
            code |= insEncodeSetFlags (id->idInsFlags());
            code |= insEncodeShiftCount(imm);
            if (fmt == IF_T2_C1)
                code |= insEncodeShiftOpts(id->idInsOpt());
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_C3:   // T2_C3   ...........S.... ....dddd....mmmm       R1  R2          S 
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        code |= insEncodeRegT2_M(id->idReg2());
        code |= insEncodeSetFlags (id->idInsFlags());
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_C7:   // T2_C7   ............nnnn ..........shmmmm       R1  R2                   imm2  
    case IF_T2_C8:   // T2_C8   ............nnnn .iii....iishmmmm       R1  R2             imm5, sh  
        sz    = SMALL_IDSC_SIZE;
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_N(id->idReg1());
        code |= insEncodeRegT2_M(id->idReg2());
        if (fmt == IF_T2_C7)
        {
            assert((imm & 0x0003) == imm);
            code |= (imm << 4);
        }
        else if (fmt == IF_T2_C8)
        {
            code |= insEncodeShiftCount(imm);
            code |= insEncodeShiftOpts(id->idInsOpt());
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_C9:   // T2_C9   ............nnnn ............mmmm       R1  R2  
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_N(id->idReg1());
        code |= insEncodeRegT2_M(id->idReg2());
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_C10:  // T2_C10  ............mmmm ....dddd....mmmm       R1  R2                     
        sz    = SMALL_IDSC_SIZE;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        code |= insEncodeRegT2_M(id->idReg2());
        code |= insEncodeRegT2_N(id->idReg2());
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_D0:   // T2_D0   ............nnnn .iiiddddii.wwwww       R1  R2             imm5, imm5  
    case IF_T2_D1:   // T2_D1   ................ .iiiddddii.wwwww       R1                 imm5, imm5   
        sz    = SMALL_IDSC_SIZE;
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        if (fmt == IF_T2_D0)
            code |= insEncodeRegT2_N(id->idReg2());
        code |= insEncodeBitFieldImm(imm);
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_E0:   // T2_E0   ............nnnn tttt......shmmmm       R1  R2  R3               imm2
    case IF_T2_E1:   // T2_E1   ............nnnn tttt............       R1  R2  
    case IF_T2_E2:   // T2_E2   ................ tttt............       R1 
#ifdef ARM_HAZARD_AVOIDANCE
        if (id->idKraitNop())
        {
            // This is a pseudo-format representing a 32-bit nop followed by ldr pc
            // First emit the nop

            dst = emitOutputNOP(dst, INS_nopw, IF_T2_A);
        }
#endif
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_T(id->idReg1());
        if (fmt == IF_T2_E0)
        {
            sz    = emitGetInstrDescSize(id);
            code |= insEncodeRegT2_N(id->idReg2());
            if (id->idIsLclVar())
            {
                code |= insEncodeRegT2_M(codeGen->rsGetRsvdReg());
                imm   = 0;
            }
            else
            {
                code |= insEncodeRegT2_M(id->idReg3());
                imm   = emitGetInsSC(id);
                assert((imm & 0x0003) == imm);
                code |= (imm << 4);
            }
        }
        else
        {
            sz    = SMALL_IDSC_SIZE;
            if (fmt != IF_T2_E2)
            {
                code |= insEncodeRegT2_N(id->idReg2());
            }
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_F1:    // T2_F1    ............nnnn ttttdddd....mmmm       R1  R2  R3  R4                   
        sz    = emitGetInstrDescSize(id);;
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_T(id->idReg1());
        code |= insEncodeRegT2_D(id->idReg2());
        code |= insEncodeRegT2_N(id->idReg3());
        code |= insEncodeRegT2_M(id->idReg4());
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_F2:    // T2_F2    ............nnnn aaaadddd....mmmm       R1  R2  R3  R4                   
        sz    = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        code |= insEncodeRegT2_N(id->idReg2());
        code |= insEncodeRegT2_M(id->idReg3());
        code |= insEncodeRegT2_T(id->idReg4());
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_G0:   // T2_G0   .......PU.W.nnnn ttttTTTTiiiiiiii       R1  R2  R3         imm8, PUW   
    case IF_T2_G1:   // T2_G1   ............nnnn ttttTTTT........       R1  R2  R3   
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_T(id->idReg1());
        code |= insEncodeRegT2_D(id->idReg2());
        code |= insEncodeRegT2_N(id->idReg3());
        if (fmt == IF_T2_G0)
        {
            sz    = emitGetInstrDescSizeSC(id);
            imm   = emitGetInsSC(id);
            assert(unsigned_abs(imm) <= 0x00ff);
            code |= abs(imm);
            code |= insEncodePUW_G0(id->idInsOpt(), imm);
        }
        else
        {
            sz    = emitGetInstrDescSize(id);
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_H0:   // T2_H0   ............nnnn tttt.PUWiiiiiiii       R1  R2             imm8, PUW  
    case IF_T2_H1:   // T2_H1   ............nnnn tttt....iiiiiiii       R1  R2             imm8 
    case IF_T2_H2:   // T2_H2   ............nnnn ........iiiiiiii       R1                 imm8  
        sz    = emitGetInstrDescSizeSC(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_T(id->idReg1());

        if (fmt != IF_T2_H2)
            code |= insEncodeRegT2_N(id->idReg2());

        if (fmt == IF_T2_H0)
        {
            assert(unsigned_abs(imm) <= 0x00ff);
            code |= insEncodePUW_H0(id->idInsOpt(), imm);
            code |= unsigned_abs(imm);
        }
        else
        {
            assert((imm & 0x00ff) == imm);
            code |= imm;
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_I0:   // T2_I0   ..........W.nnnn rrrrrrrrrrrrrrrr       R1              W, imm16  
    case IF_T2_I1:   // T2_I1   ................ rrrrrrrrrrrrrrrr                          imm16
        sz    = emitGetInstrDescSizeSC(id);
        code  = emitInsCode(ins, fmt);
        if (fmt == IF_T2_I0)
        {
            code |= insEncodeRegT2_N(id->idReg1());
            code |= (1 << 21);   //  W bit
        }
        imm   = emitGetInsSC(id);
        assert((imm & 0x3) != 0x3);
        if (imm & 0x2)
            code |= 0x8000;      //  PC bit
        if (imm & 0x1)
            code |= 0x4000;      //  LR bit
        imm >>= 2;
        assert(imm <= 0x1fff);    //  13 bits
        code |= imm;
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_K1:   // T2_K1   ............nnnn ttttiiiiiiiiiiii       R1  R2             imm12
    case IF_T2_K4:   // T2_K4   ........U....... ttttiiiiiiiiiiii       R1  PC          U, imm12  
    case IF_T2_K3:   // T2_K3   ........U....... ....iiiiiiiiiiii       PC              U, imm12 
        sz    = emitGetInstrDescSize(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        if (fmt != IF_T2_K3)
        {
            code |= insEncodeRegT2_T(id->idReg1());
        }
        if (fmt == IF_T2_K1)
        {
            code |= insEncodeRegT2_N(id->idReg2());
            assert(imm <= 0xfff);       //  12 bits
            code |= imm;
        }
        else 
        {
            assert(unsigned_abs(imm) <= 0xfff);  //  12 bits (signed)
            code |= abs(imm);
            if (imm >= 0)
                code |= (1 << 23);       //  U bit
        }
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_K2:   // T2_K2   ............nnnn ....iiiiiiiiiiii       R1                 imm12   
        sz    = emitGetInstrDescSizeSC(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_N(id->idReg1());
        assert(imm <= 0xfff);    //  12 bits
        code |= imm;
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_L0:   // T2_L0   .....i.....Snnnn .iiiddddiiiiiiii       R1  R2          S, imm8<<imm4 
    case IF_T2_L1:   // T2_L1   .....i.....S.... .iiiddddiiiiiiii       R1              S, imm8<<imm4   
    case IF_T2_L2:   // T2_L2   .....i......nnnn .iii....iiiiiiii       R1                 imm8<<imm4  
        sz    = emitGetInstrDescSize(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);

        if (fmt == IF_T2_L2)
            code |= insEncodeRegT2_N(id->idReg1());
        else
        {
            code |= insEncodeSetFlags (id->idInsFlags());
            code |= insEncodeRegT2_D(id->idReg1());
            if (fmt == IF_T2_L0)
                code |= insEncodeRegT2_N(id->idReg2());
        }
        assert(isModImmConst(imm));   // Funky ARM imm encoding
        imm   = encodeModImmConst(imm);
        assert(imm <= 0xfff);    //  12 bits
        code |= (imm & 0x00ff);
        code |= (imm & 0x0700) <<  4;
        code |= (imm & 0x0800) << 15;
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_M0:   // T2_M0   .....i......nnnn .iiiddddiiiiiiii       R1  R2             imm12                
        sz    = emitGetInstrDescSizeSC(id);
        imm   = emitGetInsSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        if (fmt == IF_T2_M0)
            code |= insEncodeRegT2_N(id->idReg2());
        imm   = emitGetInsSC(id);
        assert(imm <= 0xfff);       //  12 bits
        code |= (imm & 0x00ff);
        code |= (imm & 0x0700) <<  4;
        code |= (imm & 0x0800) << 15;
        dst  += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_N:    // T2_N    .....i......iiii .iiiddddiiiiiiii       R1                 imm16  
    case IF_T2_N2:   // T2_N2   .....i......iiii .iiiddddiiiiiiii       R1                 imm16  
        sz    = emitGetInstrDescSizeSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_D(id->idReg1());
        imm   = emitGetInsSC(id);
        if (fmt == IF_T2_N2)
        {
            assert(!id->idIsLclVar());
            assert((ins == INS_movw) || (ins == INS_movt));
            imm += (size_t)emitConsBlock;
#ifdef RELOC_SUPPORT
            if (!id->idIsCnsReloc() && !id->idIsDspReloc())
#endif
            {
                goto SPLIT_IMM;
            }
        }
        else if (id->idIsLclVar())
        {
SPLIT_IMM:
            if (ins == INS_movw)
            {
                imm &= 0xffff;
            }
            else
            {
                imm = (imm >> 16) & 0xffff;
            }
        }

#ifdef RELOC_SUPPORT
        if (id->idIsCnsReloc() || id->idIsDspReloc())
        {
            assert((ins == INS_movt) || (ins == INS_movw));
            dst  += emitOutput_Thumb2Instr(dst, code);
            if ((ins == INS_movt) && emitComp->info.compMatchedVM)
                emitRecordRelocation((void*)(dst-8), (void *)imm, IMAGE_REL_BASED_THUMB_MOV32);
        }
        else
#endif // RELOC_SUPPORT
        {
            assert((imm & 0x0000ffff) == imm);
            code |=  (imm & 0x00ff);
            code |= ((imm & 0x0700) <<  4);
            code |= ((imm & 0x0800) << 15);
            code |= ((imm & 0xf000) <<  4);
            dst  += emitOutput_Thumb2Instr(dst, code);
        }
        break;

    case IF_T2_VFP3:
        // these are the binary operators
        // d = n - m
        sz = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_VectorN(id->idReg2(), size, true);
        code |= insEncodeRegT2_VectorM(id->idReg3(), size, true);
        code |= insEncodeRegT2_VectorD(id->idReg1(), size, true);
        if (size == EA_8BYTE)
            code |= 1 << 8;
        dst += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_VFP2:
    {
        emitAttr srcSize;
        emitAttr dstSize;
        size_t   szCode = 0;

        switch (ins)
        {
        case INS_vcvt_i2d:
        case INS_vcvt_u2d:
        case INS_vcvt_f2d:
            srcSize = EA_4BYTE;
            dstSize = EA_8BYTE;
            break;

        case INS_vcvt_d2i:
        case INS_vcvt_d2u:
        case INS_vcvt_d2f:
            srcSize = EA_8BYTE;
            dstSize = EA_4BYTE;
            break;

        case INS_vmov:
        case INS_vabs:
        case INS_vsqrt:
        case INS_vcmp:
        case INS_vneg:
            if (id->idOpSize() == EA_8BYTE)
                szCode |= (1<<8); 
            __fallthrough;

        default:
            srcSize = dstSize = id->idOpSize();
            break;
        }
        
        sz    = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        code |= szCode;
        code |= insEncodeRegT2_VectorD(id->idReg1(), dstSize, true);
        code |= insEncodeRegT2_VectorM(id->idReg2(), srcSize, true);

        dst += emitOutput_Thumb2Instr(dst, code);
        break;
    }
    
    case IF_T2_VLDST:
        sz = emitGetInstrDescSizeSC(id);
        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT2_N(id->idReg2());
        code |= insEncodeRegT2_VectorD(id->idReg1(), size, true);

        imm = emitGetInsSC(id);
        if (imm < 0)
            imm = -imm; // bit 23 at 0 means negate
        else
            code |= 1 << 23; // set the positive bit

        // offset is +/- 1020
        assert(!(imm % 4));
        assert(imm >> 10 == 0);
        code |= imm >> 2;
        // bit 8 is set for doubles
        if (id->idOpSize() == EA_8BYTE)
            code |= (1<<8); 
        dst += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_VMOVD:
        // 3op assemble a double from two int regs (or back)
        sz = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        if (ins == INS_vmov_i2d)
        {
            code |= insEncodeRegT2_VectorM(id->idReg1(), size, true);
            code |= id->idReg2() << 12;
            code |= id->idReg3() << 16;
        }
        else
        {
            assert(ins == INS_vmov_d2i);
            code |= id->idReg1() << 12;
            code |= id->idReg2() << 16;
            code |= insEncodeRegT2_VectorM(id->idReg3(), size, true);
        }
        dst += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T2_VMOVS:
        // 2op assemble a float from one int reg (or back)
        sz = emitGetInstrDescSize(id);
        code  = emitInsCode(ins, fmt);
        if (ins == INS_vmov_f2i)
        {
            code |= insEncodeRegT2_VectorN(id->idReg2(), EA_4BYTE, true);
            code |= id->idReg1() << 12;
        }
        else
        {
            assert(ins == INS_vmov_i2f);
            code |= insEncodeRegT2_VectorN(id->idReg1(), EA_4BYTE, true);
            code |= id->idReg2() << 12;
        }

        dst += emitOutput_Thumb2Instr(dst, code);
        break;

    case IF_T1_J3:   // T1_J3   .....dddiiiiiiii                        R1  PC             imm8 
    case IF_T2_M1:   // T2_M1   .....i.......... .iiiddddiiiiiiii       R1  PC             imm12
        assert(id->idGCref() == GCT_NONE);
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz    = sizeof(instrDescLbl);
        break;

    case IF_T1_K:    // T1_K    ....cccciiiiiiii                       Branch              imm8, cond4                
    case IF_T1_M:    // T1_M    .....iiiiiiiiiii                       Branch              imm11                
    case IF_T2_J1:   // T2_J1   .....Scccciiiiii ..j.jiiiiiiiiiii      Branch              imm20, cond4                
    case IF_T2_J2:   // T2_J2   .....Siiiiiiiiii ..j.jiiiiiiiiii.      Branch              imm24  
    case IF_T2_N1:   // T2_N    .....i......iiii .iiiddddiiiiiiii       R1                 imm16  
    case IF_LARGEJMP:
        assert(id->idGCref() == GCT_NONE);
        assert(id->idIsBound());

        dst = emitOutputLJ(ig, dst, id);
        sz  = sizeof(instrDescJmp);
        break;

    case IF_T1_D1:   // T1_D1   .........mmmm...                       R1*   

        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_M4(id->idReg1());
        dst  += emitOutput_Thumb1Instr(dst, code);
        sz = SMALL_IDSC_SIZE;
        break;

    case IF_T1_D2:   // T1_D2   .........mmmm...                                R3* 

        /* Is this a "fat" call descriptor? */

        if  (id->idIsLargeCall())
        {
            instrDescCGCA* idCall = (instrDescCGCA*) id;
            gcrefRegs   = idCall->idcGcrefRegs;
            byrefRegs   = idCall->idcByrefRegs;
            VarSetOps::Assign(emitComp, GCvars, idCall->idcGCvars);
            sz          = sizeof(instrDescCGCA);
        }
        else
        {
            assert(!id->idIsLargeDsp());
            assert(!id->idIsLargeCns());

            gcrefRegs   = emitDecodeCallGCregs(id);
            byrefRegs   = 0;
            VarSetOps::AssignNoCopy(emitComp, GCvars, VarSetOps::MakeEmpty(emitComp));
            sz          = sizeof(instrDesc);
        }

        code  = emitInsCode(ins, fmt);
        code |= insEncodeRegT1_M4(id->idReg3());
        callInstrSize = SafeCvtAssert<unsigned char>(emitOutput_Thumb1Instr(dst, code));
        dst  += callInstrSize;
        goto DONE_CALL;

    case IF_T2_J3:   // T2_J3   .....Siiiiiiiiii ..j.jiiiiiiiiii.      Call                imm24    

#ifdef ARM_HAZARD_AVOIDANCE
        if (id->idKraitNop())
        {
            // This is a pseudo-format representing a 32-bit nop followed by unconditional call.
            // First emit the nop

            dst = emitOutputNOP(dst, INS_nopw, IF_T2_A);
        }
#endif

        /* Is this a "fat" call descriptor? */

        if  (id->idIsLargeCall())
        {
            instrDescCGCA* idCall = (instrDescCGCA*) id;
            gcrefRegs   = idCall->idcGcrefRegs;
            byrefRegs   = idCall->idcByrefRegs;
            VarSetOps::Assign(emitComp, GCvars, idCall->idcGCvars);
            sz          = sizeof(instrDescCGCA);
        }
        else
        {
            assert(!id->idIsLargeDsp());
            assert(!id->idIsLargeCns());

            gcrefRegs   = emitDecodeCallGCregs(id);
            byrefRegs   = 0;
            VarSetOps::AssignNoCopy(emitComp, GCvars, VarSetOps::MakeEmpty(emitComp));
            sz          = sizeof(instrDesc);
        }

        addr = id->idAddr()->iiaAddr;
        code = emitInsCode(ins, fmt);

#ifdef RELOC_SUPPORT
        if (id->idIsDspReloc())
        {
            callInstrSize = SafeCvtAssert<unsigned char>(emitOutput_Thumb2Instr(dst, code));
            dst  += callInstrSize;
            if (emitComp->info.compMatchedVM)
                emitRecordRelocation((void*)(dst - 4), addr, IMAGE_REL_BASED_THUMB_BRANCH24);
        }
        else
#endif // RELOC_SUPPORT
        {
            addr = (BYTE *)((size_t)addr & ~1);  // Clear the lowest bit from target address

            /* Calculate PC relative displacement */
            int  disp = addr - (dst + 4);
            bool S    = (disp < 0);
            bool I1   = ((disp & 0x00800000) == 0);
            bool I2   = ((disp & 0x00400000) == 0);

            if (S)
                code |= (1 << 26);   // S bit
            if (S ^ I1)
                code |= (1 << 13);   // J1 bit
            if (S ^ I2)
                code |= (1 << 11);   // J2 bit

            int immLo = (disp & 0x00000ffe) >>  1;
            int immHi = (disp & 0x003ff000) >> 12;

            code |= (immHi << 16);
            code |=  immLo;

            disp = abs(disp);
            assert((disp & 0x00fffffe) == disp);

            callInstrSize = SafeCvtAssert<unsigned char>(emitOutput_Thumb2Instr(dst, code));
            dst  += callInstrSize;
        }

DONE_CALL:

        /* We update the GC info before the call as the variables cannot be
           used by the call. Killing variables before the call helps with
           boundary conditions if the call is CORINFO_HELP_THROW - see bug 50029.
           If we ever track aliased variables (which could be used by the
           call), we would have to keep them alive past the call. */

        emitUpdateLiveGCvars(GCvars, *dp);

        // If the method returns a GC ref, mark R0 appropriately.
        if       (id->idGCref() == GCT_GCREF)
            gcrefRegs |= RBM_R0;
        else if  (id->idGCref() == GCT_BYREF)
            byrefRegs |= RBM_R0;

        // If the GC register set has changed, report the new set.
        if  (gcrefRegs != emitThisGCrefRegs)
            emitUpdateLiveGCregs(GCT_GCREF, gcrefRegs, dst);

        if  (byrefRegs != emitThisByrefRegs)
            emitUpdateLiveGCregs(GCT_BYREF, byrefRegs, dst);
     
        // Some helper calls may be marked as not requiring GC info to be recorded.
        if  ((!id->idIsNoGC()))
        {
            // On ARM, as on AMD64, we don't change the stack pointer to push/pop args.
            // So we're not really doing a "stack pop" here (note that "args" is 0), but we use this mechanism
            // to record the call for GC info purposes.  (It might be best to use an alternate call,
            // and protect "emitStackPop" under the EMIT_TRACK_STACK_DEPTH preprocessor variable.)
            emitStackPop(dst, /*isCall*/true, callInstrSize, /*args*/0);
      
            /* Do we need to record a call location for GC purposes? */

            if  (!emitFullGCinfo)
            {
                emitRecordGCcall(dst, callInstrSize);
            }
        }

        break;

        /********************************************************************/
        /*                            oops                                  */
        /********************************************************************/

    default:

#ifdef  DEBUG
        printf("unexpected format %s\n", emitIfName(id->idInsFmt()));
        assert(!"don't know how to encode this instruction");
#endif
        break;
    }

    // Determine if any registers now hold GC refs, or whether a register that was overwritten held a GC ref.
    // We assume here that "id->idGCref()" is not GC_NONE only if the instruction described by "id" writes a
    // GC ref to register "id->idReg1()".  (It may, apparently, also not be GC_NONE in other cases, such as
    // for stores, but we ignore those cases here.)
    if (emitInsMayWriteToGCReg(id))  // True if "id->idIns()" writes to a register than can hold GC ref.
    {
        // If we ever generate instructions that write to multiple registers (LDM, or POP),
        // then we'd need to more work here to ensure that changes in the status of GC refs are
        // tracked properly.
        if (emitInsMayWriteMultipleRegs(id))
        {
            // We explicitly list the multiple-destination-target instruction that we expect to
            // be emitted outside of the prolog and epilog here.
            switch (ins)
            {
            case INS_smull: case INS_umull:
            case INS_smlal: case INS_umlal:
            case INS_vmov_d2i:
                // For each of these, idReg1() and idReg2() are the destination registers.
                emitGCregDeadUpd(id->idReg1(), dst);
                emitGCregDeadUpd(id->idReg2(), dst);
                break;
            default:
                assert(false);  // We need to recognize this multi-target instruction...
            }
        }
        else
        {
            if (id->idGCref() != GCT_NONE)
            {
                emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst);
            }
            else
            {
                // I also assume that "idReg1" is the destination register of all instructions that write to registers.
                emitGCregDeadUpd(id->idReg1(), dst);
            }
        }
    }

    // Now we determine if the instruction has written to a (local variable) stack location, and either written a GC ref or
    // overwritten one.
    if (emitInsWritesToLclVarStackLoc(id))
    {
        int varNum = id->idAddr()->iiaLclVar.lvaVarNum();
        unsigned ofs    = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), sizeof(size_t));
        regNumber regBase;
        int adr = emitComp->lvaFrameAddress(varNum, true, &regBase, ofs);
        if (id->idGCref() != GCT_NONE)
        {
            emitGCvarLiveUpd(adr + ofs, varNum, id->idGCref(), dst);
        }
        else
        {
            // If the type of the local is a gc ref type, update the liveness.
            var_types vt;
            if (varNum >= 0)
            {
                // "Regular" (non-spill-temp) local.
                vt = var_types(emitComp->lvaTable[varNum].lvType);
            }
            else
            {
                TempDsc* tmpDsc = emitComp->tmpFindNum(varNum);
                vt = tmpDsc->tdTempType();
            }
            if (vt == TYP_REF || vt == TYP_BYREF)
                emitGCvarDeadUpd(adr + ofs, dst);
        }
    }

#ifdef  DEBUG
    /* Make sure we set the instruction descriptor size correctly */

    size_t expected = emitSizeOfInsDsc(id);
    assert(sz == expected);

    if  (emitComp->opts.disAsm || emitComp->opts.dspEmit || emitComp->verbose)
    {
        emitDispIns(id, false, dspOffs, true, emitCurCodeOffs(odst), *dp, (dst-*dp), ig);
    }

    if (emitComp->compDebugBreak)
    {
        // set JitEmitPrintRefRegs=1 will print out emitThisGCrefRegs and emitThisByrefRegs
        // at the beginning of this method.
        static ConfigDWORD fJitEmitPrintRefRegs;
        if (fJitEmitPrintRefRegs.val(CLRConfig::INTERNAL_JitEmitPrintRefRegs) != 0) {
            printf("Before emitOutputInstr for id->idDebugOnlyInfo()->idNum=0x%02x\n", id->idDebugOnlyInfo()->idNum);
            printf("  emitThisGCrefRegs(0x%p)=", dspPtr(&emitThisGCrefRegs));
                printRegMaskInt(emitThisGCrefRegs);
                emitDispRegSet (emitThisGCrefRegs);
                printf("\n");
            printf("  emitThisByrefRegs(0x%p)=", dspPtr(&emitThisByrefRegs));
                printRegMaskInt(emitThisByrefRegs);
                emitDispRegSet (emitThisByrefRegs);
                printf("\n");
        }        

        // For example, set JitBreakEmitOutputInstr=a6 will break when this method is called for
        // emitting instruction a6, (i.e. IN00a6 in jitdump).
        static ConfigDWORD fJitBreakEmitOutputInstr;
        if ((unsigned)fJitBreakEmitOutputInstr.val(CLRConfig::INTERNAL_JitBreakEmitOutputInstr) == id->idDebugOnlyInfo()->idNum)
        {
            assert(!"JitBreakEmitOutputInstr reached");
        }
    }
#endif

    /* All instructions are expected to generate code */

    assert(*dp != dst);

    *dp = dst;

    return  sz;
}


/*****************************************************************************/
/*****************************************************************************/

#ifdef DEBUG

static bool        insAlwaysSetFlags(instruction ins)
{
    bool result = false;
    switch (ins)
    {
    case INS_cmp:
    case INS_cmn:
    case INS_teq:
    case INS_tst:
        result = true;
        break;
    }
    return result;
}

/*****************************************************************************
 *
 *  Display the instruction name, optionally the instruction
 *   can add the "s" suffix if it must set the flags.
 */
void                emitter::emitDispInst(instruction ins, insFlags flags)
{
    const char *  insstr = codeGen->genInsName(ins);
    int           len    = strlen(insstr);

    /* Display the instruction name */

    printf("%s", insstr);
    if (insSetsFlags(flags) && !insAlwaysSetFlags(ins))
    {
        printf("s");
        len++;
    }

    //
    // Add at least one space after the instruction name
    // and add spaces until we have reach the normal size of 8
    do {
       printf(" "); 
       len++;
    }
    while (len < 8);
}

/*****************************************************************************
 *
 *  Display an reloc value
 *  If we are formatting for an assembly listing don't print the hex value
 *  since it will prevent us from doing assembly diffs
 */
void                emitter::emitDispReloc(int value, bool addComma)
{
    if (emitComp->opts.disAsm)
    {
        printf("(reloc)");
    }
    else
    {
        printf("(reloc 0x%x)", dspPtr(value));
    }

    if (addComma)
        printf(", ");
}

#define STRICT_ARM_ASM 0

/*****************************************************************************
 *
 *  Display an immediate value
 */
void                emitter::emitDispImm(int imm, bool addComma, bool alwaysHex /* =false */)
{
    if (!alwaysHex && (imm > -1000) && (imm < 1000))
        printf("%d", imm);
    else if ((imm > 0) || (imm == -imm) ||    // -0x80000000 == 0x80000000. So we don't want to add an extra "-" at the beginning.
             (emitComp->opts.disDiffable && (imm == 0xD1FFAB1E))) // Don't display this as negative
        printf("0x%02x", imm);
    else // val <= -1000
        printf("-0x%02x", -imm);

    if (addComma)
        printf(", ");
}

/*****************************************************************************
 *
 *  Display an arm condition for the IT instructions
 */
void                emitter::emitDispCond(int cond)
{
    const static char* armCond[16] = { "eq", "ne", "hs", "lo", 
                                       "mi", "pl", "vs", "vc",
                                       "hi", "ls", "ge", "lt",
                                       "gt", "le", "AL", "NV" };  // The last two are invalid 
    assert(0 <= cond && (unsigned)cond < ArrLen(armCond));
    printf(armCond[cond]);
}

/*****************************************************************************
 *
 *  Display a register range in a range format
 */
void                emitter::emitDispRegRange(regNumber reg, int len, emitAttr attr)
{
    printf("{");
    emitDispReg(reg, attr, false);
    if (len > 1)
    {
        printf("-");
        emitDispReg((regNumber) (reg + len - 1), attr, false);
    }
    printf("}");
}

/*****************************************************************************
 *
 *  Display an register mask in a list format
 */
void                emitter::emitDispRegmask(int imm, bool encodedPC_LR)
{
    bool printedOne = false;
    bool hasPC;
    bool hasLR;

    if (encodedPC_LR)
    {
        hasPC = (imm & 2) != 0;
        hasLR = (imm & 1) != 0;
        imm >>= 2;
    }
    else
    {
        hasPC  = (imm & RBM_PC) != 0;
        hasLR  = (imm & RBM_LR) != 0;
        imm   &= ~(RBM_PC | RBM_LR);
    }

    regNumber reg = REG_R0;
    unsigned bit = 1;

    printf("{");
    while (imm != 0)
    {
        if (bit & imm)
        {
            if (printedOne)
                printf(",");
            printf("%s", emitRegName(reg));
            printedOne = true;
            imm -= bit;
        }

        reg = regNumber (reg + 1);
        bit <<= 1;
    }

    if (hasLR)
    {
        if (printedOne)
            printf(",");
        printf("%s", emitRegName(REG_LR));
        printedOne = true;
    }

    if (hasPC)
    {
        if (printedOne)
            printf(",");
        printf("%s", emitRegName(REG_PC));
        printedOne = true;
    }
    printf("}");
}

/*****************************************************************************
 *
 *  Returns the encoding for the Shift Type bits to be used in a Thumb-2 encoding
 */

void                emitter::emitDispShiftOpts(insOpts opt)
{
    if (opt == INS_OPTS_LSL)
        printf(" LSL ");
    else if (opt == INS_OPTS_LSR)
        printf(" LSR ");
    else if (opt == INS_OPTS_ASR)
        printf(" ASR ");
    else if (opt == INS_OPTS_ROR)
        printf(" ROR ");
    else if (opt == INS_OPTS_RRX)
        printf(" RRX ");
} 


/*****************************************************************************
 *
 *  Display a register
 */
void                emitter::emitDispReg(regNumber reg, emitAttr  attr, bool addComma)
{
    if (isFloatReg(reg))
    {
        char *size = attr == EA_8BYTE ? "d" : "s";
        printf("%s%s", size, emitFloatRegName(reg, attr)+1);
    }
    else
    {
        printf("%s", emitRegName(reg, attr));
    }

    if (addComma)
        printf(", ");
}

void emitter::emitDispFloatReg(regNumber reg, emitAttr attr, bool addComma)
{
}

/*****************************************************************************
 *
 *  Display an addressing operand [reg]
 */
void                emitter::emitDispAddrR(regNumber reg, emitAttr  attr)
{
    printf("[");
    emitDispReg(reg, attr, false);
    printf("]");
    emitDispGC(attr);
}

/*****************************************************************************
 *
 *  Display an addressing operand [reg + imm]
 */
void                emitter::emitDispAddrRI(regNumber reg, int imm, emitAttr  attr)
{
    bool regIsSPorFP = (reg == REG_SP) || (reg == REG_FP);

    printf("[");
    emitDispReg(reg, attr, false);
    if (imm != 0)
    {
        if (imm >= 0)
        {
#if STRICT_ARM_ASM
            printf(", ");
#else
            printf("+");
#endif
        }
        emitDispImm(imm, false, regIsSPorFP);
    }
    printf("]");
    emitDispGC(attr);
}

/*****************************************************************************
 *
 *  Display an addressing operand [reg + reg]
 */
void                emitter::emitDispAddrRR(regNumber reg1, regNumber reg2, emitAttr  attr)
{
    printf("[");
    emitDispReg(reg1, attr, false);
#if STRICT_ARM_ASM
    printf(", ");
#else
    printf("+");
#endif
    emitDispReg(reg2, attr, false);
    printf("]");
    emitDispGC(attr);
}

/*****************************************************************************
 *
 *  Display an addressing operand [reg + reg * imm]
 */
void                emitter::emitDispAddrRRI(regNumber reg1, regNumber reg2, 
                                             int imm, emitAttr  attr)
{
    printf("[");
    emitDispReg(reg1, attr, false);
#if STRICT_ARM_ASM
    printf(", ");
    emitDispReg(reg2, attr, false);
    if (imm > 0)
    {
        printf(" LSL ");
        emitDispImm(1 << imm, false);
    }
#else
    printf("+");
    if (imm > 0)
    {
        emitDispImm(1 << imm, false);
        printf("*");
    }
    emitDispReg(reg2, attr, false);
#endif
    printf("]");
    emitDispGC(attr);
}

/*****************************************************************************
 *
 *  Display an addressing operand [reg + imm]
 */
void                emitter::emitDispAddrPUW(regNumber reg, int imm, 
                                             insOpts  opt, emitAttr  attr)
{
    bool regIsSPorFP = (reg == REG_SP) || (reg == REG_FP);

    printf("[");
    emitDispReg(reg, attr, false);
    if (insOptAnyInc(opt))
        printf("!");

    if (imm != 0)
    {
        if (imm >= 0)
        {
#if STRICT_ARM_ASM
            printf(", ");
#else
            printf("+");
#endif
        }
        emitDispImm(imm, false, regIsSPorFP);
    }
    printf("]");

    emitDispGC(attr);
}

/*****************************************************************************
 *
 *  Display the gc-ness of the operand 
 */
void                emitter::emitDispGC(emitAttr  attr)
{
#if 0
    // TODO-ARM-Cleanup: Fix or delete.
    if (attr == EA_GCREF)
        printf(" @gc");
    else if (attr == EA_BYREF)
        printf(" @byref");
#endif
}

/*****************************************************************************
 *
 *  Display (optionally) the instruction encoding in hex 
 */

void                emitter::emitDispInsHex(BYTE *  code, size_t sz)
{
    // We do not display the instruction hex if we want diff-able disassembly
    if (!emitComp->opts.disDiffable)
    {
        if (sz == 2)
        {
            printf("  %04X     ", (*((unsigned short *) code)));
        }
        else if (sz == 4)
        {
            printf("  %04X %04X", (*((unsigned short *) (code+0))),
                                  (*((unsigned short *) (code+2))));
        }
    }
}

/****************************************************************************
 *
 *  Display the given instruction.
 */

void                emitter::emitDispInsHelp(instrDesc *  id, 
                                             bool         isNew, 
                                             bool         doffs, 
                                             bool         asmfm,
                                             unsigned     offset,
                                             BYTE *       code,
                                             size_t       sz,
                                             insGroup *   ig)
{
    if (EMITVERBOSE)
    {
        unsigned  idNum = id->idDebugOnlyInfo()->idNum;    // Do not remove this!  It is needed for VisualStudio conditional breakpoints

        printf("IN%04x: ", idNum);
    }

    if (code == NULL)
        sz = 0;

    if  (!emitComp->opts.dspEmit && !isNew && !asmfm && sz)
        doffs = true;

    /* Display the instruction offset */

    emitDispInsOffs(offset, doffs);

    /* Display the instruction hex code */

    emitDispInsHex(code, sz);

    printf("      ");

    /* Get the instruction and format */

    instruction   ins = id->idIns();
    insFormat     fmt = id->idInsFmt();

    emitDispInst(ins, id->idInsFlags());

    /* If this instruction has just been added, check its size */

    assert(isNew == false || (int)emitSizeOfInsDsc(id) == emitCurIGfreeNext - (BYTE*)id);

    /* Figure out the operand size */
    emitAttr  attr;
    if       (id->idGCref() == GCT_GCREF)
        attr = EA_GCREF;
    else if  (id->idGCref() == GCT_BYREF)
        attr = EA_BYREF;
    else
        attr = id->idOpSize();

    switch (fmt)
    {
        int             imm;
        int             offs;
        const char  *   methodName;

    case IF_T1_A:   // None
    case IF_T2_A:
        break;

    case IF_T1_L0:  // Imm
    case IF_T2_B:
        emitDispImm(emitGetInsSC(id), false);
        break;

    case IF_T1_B:   // <cond>
        emitDispCond(emitGetInsSC(id));
        break;

    case IF_T1_L1:  // <regmask8>
    case IF_T2_I1:  // <regmask16>
        emitDispRegmask(emitGetInsSC(id), true);
        break;

    case IF_T2_E2:  // Reg
        if (id->idIns() == INS_vmrs)
        {
            if (id->idReg1() != REG_R15)
            {
                emitDispReg(id->idReg1(), attr, true);
                printf("FPSCR");
            }
            else
            {
                printf("APSR, FPSCR");
            }
        }
        else
        {
            emitDispReg(id->idReg1(), attr, false);
        }
        break;

    case IF_T1_D1:
        emitDispReg(id->idReg1(), attr, false);
        break;

    case IF_T1_D2:
        emitDispReg(id->idReg3(), attr, false);
        {
            CORINFO_METHOD_HANDLE handle = (CORINFO_METHOD_HANDLE) id->idDebugOnlyInfo()->idMemCookie;
            if (handle != 0)
            {
                methodName = emitComp->eeGetMethodFullName(handle);
                printf("\t\t// %s", methodName);
            }
        }
        break;

    case IF_T1_F:   // SP, Imm
        emitDispReg(REG_SP, attr, true);
        emitDispImm(emitGetInsSC(id), false);
        break;

    case IF_T1_J0:  // Reg, Imm
    case IF_T2_L1:
    case IF_T2_L2:
    case IF_T2_N:
        emitDispReg(id->idReg1(), attr, true);
        imm   = emitGetInsSC(id);
        if (fmt == IF_T2_N)
        {
            if (emitComp->opts.disDiffable)
                imm = 0xD1FF;
#if RELOC_SUPPORT
            if (id->idIsCnsReloc() || id->idIsDspReloc())
            {
                if (emitComp->opts.disDiffable)
                    imm = 0xD1FFAB1E;
                printf("%s RELOC ", (id->idIns() == INS_movw) ? "LOW" : "HIGH");
            }
#endif // RELOC_SUPPORT
        }
        emitDispImm(imm, false, (fmt == IF_T2_N));
        break;

    case IF_T2_N2:
        emitDispReg(id->idReg1(), attr, true);
        imm   = emitGetInsSC(id);
        {
            dataSection *   jdsc = 0;
            NATIVE_OFFSET   offs = 0;

            /* Find the appropriate entry in the data section list */

            for (jdsc = emitConsDsc.dsdList;
                 jdsc;
                 jdsc = jdsc->dsNext)
            {
                UNATIVE_OFFSET  size = jdsc->dsSize;

                /* Is this a label table? */

                if  (jdsc->dsType == dataSection::blockAbsoluteAddr)
                {
                    if (offs == imm)
                        break;
                }

                offs += size;
            }

            assert(jdsc != NULL);

#ifdef RELOC_SUPPORT
            if (id->idIsDspReloc())
            {
                printf("reloc ");
            }
#endif
            printf("%s ADDRESS J_M%03u_DS%02u", (id->idIns() == INS_movw) ? "LOW" : "HIGH",
                                                 Compiler::s_compMethodsCount, 
                                                 imm);

            // After the MOVT, dump the table
            if  (id->idIns() == INS_movt)
            {
                unsigned        cnt = jdsc->dsSize / TARGET_POINTER_SIZE;
                BasicBlock  * * bbp = (BasicBlock**)jdsc->dsCont;

                bool            isBound = (emitCodeGetCookie(*bbp) != NULL);

                if (isBound)
                {
                    printf("\n\n    J_M%03u_DS%02u LABEL   DWORD", Compiler::s_compMethodsCount, imm);

                    /* Display the label table (it's stored as "BasicBlock*" values) */

                    do
                    {
                        insGroup    *   lab;

                        /* Convert the BasicBlock* value to an IG address */

                        lab = (insGroup*)emitCodeGetCookie(*bbp++); assert(lab);

                        printf("\n            DD      G_M%03u_IG%02u", Compiler::s_compMethodsCount, lab->igNum);
                    }
                    while (--cnt);
                }
            }
        }
        break;

    case IF_T2_H2:   // [Reg+imm]
    case IF_T2_K2:
        emitDispAddrRI(id->idReg1(), emitGetInsSC(id), attr);
        break;

    case IF_T2_K3:  // [PC+imm]
        emitDispAddrRI(REG_PC, emitGetInsSC(id), attr);
        break;

    case IF_T1_J1:  // reg, <regmask8>
    case IF_T2_I0:  // reg, <regmask16>
        emitDispReg(id->idReg1(), attr, false);
        printf("!, ");
        emitDispRegmask(emitGetInsSC(id), false);
        break;

    case IF_T1_D0:  // Reg, Reg
    case IF_T1_E:
    case IF_T2_C3:
    case IF_T2_C9:
    case IF_T2_C10:
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, false);
        if (fmt == IF_T1_E && id->idIns() == INS_rsb)
        {
            printf(", 0");
        }
        break;

    case IF_T2_E1:   // Reg, [Reg]
        emitDispReg(id->idReg1(), attr, true);
        emitDispAddrR(id->idReg2(), attr);
        break;

    case IF_T2_D1:  // Reg, Imm, Imm
        emitDispReg(id->idReg1(), attr, true);
        imm = emitGetInsSC(id);
        {
            int lsb = (imm >> 5) & 0x1f;
            int msb = imm & 0x1f;
            int imm1 = lsb;
            int imm2 = msb + 1 - lsb;
            emitDispImm(imm1, true);
            emitDispImm(imm2, false);
        }
        break;

    case IF_T1_C:  // Reg, Reg, Imm
    case IF_T1_G:
    case IF_T2_C2:
    case IF_T2_H1:
    case IF_T2_K1:
    case IF_T2_L0:
    case IF_T2_M0:
        emitDispReg(id->idReg1(), attr, true);
        imm = emitGetInsSC(id);
        if (emitInsIsLoadOrStore(ins))
        {
            emitDispAddrRI(id->idReg2(), imm, attr);
        }
        else
        {
            emitDispReg(id->idReg2(), attr, true);
            emitDispImm(imm, false);
        }
        break;

    case IF_T1_J2:
        emitDispReg(id->idReg1(), attr, true);
        imm = emitGetInsSC(id);
        if (emitInsIsLoadOrStore(ins))
        {
            emitDispAddrRI(REG_SP, imm, attr);
        }
        else
        {
            emitDispReg(REG_SP, attr, true);
            emitDispImm(imm, false);
        }
        break;

    case IF_T2_K4:
        emitDispReg(id->idReg1(), attr, true);
        emitDispAddrRI(REG_PC, emitGetInsSC(id), attr);
        break;

    case IF_T2_C1:
    case IF_T2_C8:
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, false);
        imm = emitGetInsSC(id);
        if (id->idInsOpt() == INS_OPTS_RRX)
        {
            emitDispShiftOpts(id->idInsOpt()); 
            assert(imm == 1);
        }
        else if (imm > 0)
        {
            emitDispShiftOpts(id->idInsOpt());            
            emitDispImm(imm, false);
        }
        break;

    case IF_T2_C6:
        imm = emitGetInsSC(id);
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, (imm != 0));
        if (imm != 0)
        {
            emitDispImm(imm, false);
        }
        break;

    case IF_T2_C7:
        emitDispAddrRRI(id->idReg1(), id->idReg2(), emitGetInsSC(id), attr);
        break;

    case IF_T2_H0:
        emitDispReg(id->idReg1(), attr, true);
        emitDispAddrPUW(id->idReg2(), emitGetInsSC(id), id->idInsOpt(), attr);
        break;

    case IF_T1_H:   // Reg, Reg, Reg
        emitDispReg(id->idReg1(), attr, true);
        if (emitInsIsLoadOrStore(ins))
        {
            emitDispAddrRR(id->idReg2(), id->idReg3(), attr);
        }
        else
        {
            emitDispReg(id->idReg2(), attr, true);
            emitDispReg(id->idReg3(), attr, false);
        }
        break;

    case IF_T2_C4:
    case IF_T2_C5:
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, true);
        emitDispReg(id->idReg3(), attr, false);
        break;

    case IF_T2_VFP3:
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, true);
        emitDispReg(id->idReg3(), attr, false);
        break;

    case IF_T2_VFP2:
        switch (id->idIns())
        {
        case INS_vcvt_d2i:
        case INS_vcvt_d2u:
        case INS_vcvt_d2f:
            emitDispReg(id->idReg1(), EA_4BYTE, true);
            emitDispReg(id->idReg2(), EA_8BYTE, false);
            break;

        case INS_vcvt_i2d:
        case INS_vcvt_u2d:
        case INS_vcvt_f2d:
            emitDispReg(id->idReg1(), EA_8BYTE, true);
            emitDispReg(id->idReg2(), EA_4BYTE, false);
            break;

            // we just use the type on the instruction 
            // unless it is an asymmetrical one like the converts
        default:
            emitDispReg(id->idReg1(), attr, true);
            emitDispReg(id->idReg2(), attr, false);
            break;
        }
        break;

    case IF_T2_VLDST: 
        imm = emitGetInsSC(id);
        switch (id->idIns())
        {
            case INS_vldr:
            case INS_vstr:
                emitDispReg(id->idReg1(), attr, true);
                emitDispAddrPUW(id->idReg2(), imm, id->idInsOpt(), attr);
                break;

            case INS_vldm:
            case INS_vstm:
                emitDispReg(id->idReg2(), attr, false);
                if (insOptAnyInc(id->idInsOpt()))
                    printf("!");
                printf(", ");
                emitDispRegRange(id->idReg1(), abs(imm) >> 2, attr);
                break;

            case INS_vpush:
            case INS_vpop:
                emitDispRegRange(id->idReg1(), abs(imm) >> 2, attr);
                break;

            default:
                unreached();
        }
        break;

    case IF_T2_VMOVD:
        switch (id->idIns())
        {
        case INS_vmov_i2d:
            emitDispReg(id->idReg1(), attr,     true);  // EA_8BYTE
            emitDispReg(id->idReg2(), EA_4BYTE, true);
            emitDispReg(id->idReg3(), EA_4BYTE, false);
            break;
        case INS_vmov_d2i:
            emitDispReg(id->idReg1(), EA_4BYTE, true);
            emitDispReg(id->idReg2(), EA_4BYTE, true);
            emitDispReg(id->idReg3(), attr,     false); // EA_8BYTE
            break;
        default: unreached();
        }
        break;

    case IF_T2_VMOVS:
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, false);
        break;

    case IF_T2_G1:
        emitDispReg(id->idReg1(), attr, true);
        emitDispAddrRR(id->idReg2(), id->idReg3(), attr);
        break;
        
    case IF_T2_D0:         // Reg, Reg, Imm, Imm
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, true);
        imm = emitGetInsSC(id);
        if (ins == INS_bfi)
        {
            int lsb  = (imm >> 5) & 0x1f;
            int msb  = imm & 0x1f;
            int imm1 = lsb;
            int imm2 = msb + 1 - lsb;
            emitDispImm(imm1, true);
            emitDispImm(imm2, false);
        }
        else
        {
            int lsb     = (imm >> 5) & 0x1f;
            int widthm1 = imm & 0x1f;
            int imm1    = lsb;
            int imm2    = widthm1 + 1;
            emitDispImm(imm1, true);
            emitDispImm(imm2, false);
        }
        break;
        
    case IF_T2_C0:          // Reg, Reg, Reg, Imm
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, true);
        emitDispReg(id->idReg3(), attr, false);
        imm = emitGetInsSC(id);
        if (id->idInsOpt() == INS_OPTS_RRX)
        {
            emitDispShiftOpts(id->idInsOpt()); 
            assert(imm == 1);
        }
        else if (imm > 0)
        {
            emitDispShiftOpts(id->idInsOpt());            
            emitDispImm(imm, false);
        }
        break;

    case IF_T2_E0:
        emitDispReg(id->idReg1(), attr, true);
        if (id->idIsLclVar())
        {
            emitDispAddrRRI(id->idReg2(), codeGen->rsGetRsvdReg(), 0, attr);
        }
        else
        {
            emitDispAddrRRI(id->idReg2(), id->idReg3(), emitGetInsSC(id), attr);
        }
        break;

    case IF_T2_G0:
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, true);
        emitDispAddrPUW(id->idReg3(), emitGetInsSC(id), id->idInsOpt(), attr);
        break;

    case IF_T2_F1:   // Reg, Reg, Reg, Reg
    case IF_T2_F2:  
        emitDispReg(id->idReg1(), attr, true);
        emitDispReg(id->idReg2(), attr, true);
        emitDispReg(id->idReg3(), attr, true);
        emitDispReg(id->idReg4(), attr, false);
        break;

    case IF_T1_J3:
    case IF_T2_M1:         // Load Label
        emitDispReg(id->idReg1(), attr, true);
        if  (id->idIsBound())
            printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
        else
            printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
        break;

    case IF_T1_I:          // Special Compare-and-branch
        emitDispReg(id->idReg1(), attr, true);
        __fallthrough;

    case IF_T1_K:          // Special Branch, conditional
    case IF_T1_M:
        assert(((instrDescJmp*)id)->idjShort);
        printf("SHORT ");
        __fallthrough;

    case IF_T2_N1:
        if (fmt == IF_T2_N1)
        {
            emitDispReg(id->idReg1(), attr, true);
            printf("%s ADDRESS ", (id->idIns()==INS_movw)?"LOW":"HIGH");
        }
        __fallthrough;
        
    case IF_T2_J1:
    case IF_T2_J2:
    case IF_LARGEJMP:
        {
            if (id->idAddr()->iiaHasInstrCount())
            {
                int instrCount = id->idAddr()->iiaGetInstrCount();

                if (ig == NULL)
                {
                    printf("pc%s%d instructions", (instrCount >= 0) ? "+" : "" , instrCount);
                }
                else
                {
                    unsigned insNum        = emitFindInsNum(ig, id);
                    UNATIVE_OFFSET srcOffs = ig->igOffs + emitFindOffset(ig, insNum + 1);
                    UNATIVE_OFFSET dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount);
                    ssize_t relOffs = (ssize_t) (emitOffsetToPtr(dstOffs) - emitOffsetToPtr(srcOffs));            
                    printf("pc%s%d (%d instructions)", (relOffs >= 0) ? "+" : "" , relOffs, instrCount);
                }
            }
            else if  (id->idIsBound())
                printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum);
            else
                printf("L_M%03u_BB%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaBBlabel->bbNum);
        }
        break;

    case IF_T2_J3:
        if (id->idIsCallAddr())
        {
            offs = (ssize_t)id->idAddr()->iiaAddr;
            methodName = "";
        }
        else
        {
            offs = 0;
            methodName = emitComp->eeGetMethodFullName((CORINFO_METHOD_HANDLE)id->idDebugOnlyInfo()->idMemCookie);
        }

        if (offs)
        {
            if (id->idIsDspReloc())
                printf("reloc ");
            printf("%08X", offs);
        }
        else
        {
            printf("%s", methodName);
        }

        break;

    default:
        printf("unexpected format %s", emitIfName(id->idInsFmt()));
        assert(!"unexpectedFormat");
        break;
    }

    if (id->idDebugOnlyInfo()->idVarRefOffs)
    {
        printf("\t// ");
        emitDispFrameRef(id->idAddr()->iiaLclVar.lvaVarNum(),
                         id->idAddr()->iiaLclVar.lvaOffset(),
                         id->idDebugOnlyInfo()->idVarRefOffs, asmfm);
    }

    printf("\n");
}

void                emitter::emitDispIns(instrDesc *  id, 
                                         bool         isNew, 
                                         bool         doffs, 
                                         bool         asmfm,
                                         unsigned     offset,
                                         BYTE *       code,
                                         size_t       sz,
                                         insGroup *   ig)
{
    insFormat     fmt = id->idInsFmt();

#ifdef ARM_HAZARD_AVOIDANCE
    if (id->idKraitNop())  
    {
        assert(id->idIsKraitBranch());

        // We will display a nop.w unless we have an unbound INS_b instruction
        //
        // Most unbound INS_b instructions will be resolve to short jumps and thus
        // we don't display the nop.w while they are unbound. If they are bound and
        // are still marked with idKraitNop they will display the nop.w
        //
        if ((id->idIns() != INS_b) || id->idIsBound())
        {
            // First, display INS_nopw. Construct a temporary instrDesc to represent it, since
            // there doesn't exist an actual one in the stream.

            instrDesc idNOP;
            memset(&idNOP, 0, sizeof(idNOP));

            instrDesc* pidNOP = &idNOP;

            pidNOP->idIns(INS_nopw);
            pidNOP->idInsFmt(IF_T2_A);
            pidNOP->idInsSize(emitInsSize(IF_T2_A));
            pidNOP->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field

            size_t nopSizeOrZero = (code == NULL) ? 0 : 4;    // NOPW is 4 bytes
            emitDispInsHelp(pidNOP, false, doffs, asmfm, offset, code, nopSizeOrZero, ig); 

            code   += nopSizeOrZero;
            sz     -= nopSizeOrZero;
            offset += 4;
        }
    }
#endif

    /* Special-case IF_LARGEJMP */

    if ((fmt == IF_LARGEJMP) && id->idIsBound())   
    {
        // This is a pseudo-instruction format representing a large conditional branch. See the comment
        // in emitter::emitOutputLJ() for the full description.
        //
        // For this pseudo-instruction, we will actually generate:
        //
        //      b<!cond> L_not  // 2 bytes. Note that we reverse the condition.
        //      b L_target      // 4 bytes
        //   L_not:
        // 
        // These instructions don't exist in the actual instruction stream, so we need to fake them
        // up to display them.
        //
        // Note: don't touch the actual instrDesc. If we accidentally messed it up, it would create a very
        // difficult to find bug.

        instrDescJmp idJmp;
        instrDescJmp* pidJmp = &idJmp;

        memset(&idJmp, 0, sizeof(idJmp));

        pidJmp->idIns(emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(id->idIns())))); // reverse the conditional instruction
        pidJmp->idInsFmt(IF_T1_K);
        pidJmp->idInsSize(emitInsSize(IF_T1_K));
        pidJmp->idjShort = 1;
        pidJmp->idAddr()->iiaSetInstrCount(1);
        pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field

        size_t   bcondSizeOrZero = (code == NULL) ? 0 : 2;    // branch is 2 bytes
        emitDispInsHelp(pidJmp, false, doffs, asmfm, offset, code, bcondSizeOrZero, NULL /* force display of pc-relative branch */);

        code   += bcondSizeOrZero;
        offset += 2;

        // Next, display the unconditional branch

        // Reset the local instrDesc
        memset(&idJmp, 0, sizeof(idJmp));

        pidJmp->idIns(INS_b);
        pidJmp->idInsFmt(IF_T2_J2);
        pidJmp->idInsSize(emitInsSize(IF_T2_J2));
        pidJmp->idjShort = 0;
        if  (id->idIsBound())
        {
            pidJmp->idSetIsBound();
            pidJmp->idAddr()->iiaIGlabel = id->idAddr()->iiaIGlabel;
        }
        else
        {
            pidJmp->idAddr()->iiaBBlabel = id->idAddr()->iiaBBlabel;
        }
        pidJmp->idDebugOnlyInfo(id->idDebugOnlyInfo()); // share the idDebugOnlyInfo() field

        size_t   brSizeOrZero = (code == NULL) ? 0 : 4;   // unconditional branch is 4 bytes
        emitDispInsHelp(pidJmp, isNew, doffs, asmfm, offset, code, brSizeOrZero, ig); 
    }
    else
    {
        emitDispInsHelp(id, isNew, doffs, asmfm, offset, code, sz, ig);
    }
}

/*****************************************************************************
 *
 *  Display a stack frame reference.
 */

void                emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm)
{
    printf("[");

        if  (varx < 0)
            printf("TEMP_%02u", -varx);
        else
            emitComp->gtDispLclVar(+varx, false);

        if (disp < 0)
                printf("-0x%02x", -disp);
        else if (disp > 0)
                printf("+0x%02x", +disp);

    printf("]");

    if  (varx >= 0 && emitComp->opts.varNames)
    {
        LclVarDsc*  varDsc;
        const   char *        varName;

        assert((unsigned)varx < emitComp->lvaCount);
        varDsc  = emitComp->lvaTable + varx;
        varName = emitComp->compLocalVarName(varx, offs);

        if  (varName)
        {
            printf("'%s", varName);

            if      (disp < 0)
                    printf("-%d", -disp);
            else if (disp > 0)
                    printf("+%d", +disp);

            printf("'");
        }
    }
}


#endif // DEBUG

#ifndef LEGACY_BACKEND

// this is very similar to emitInsBinary and probably could be folded in to same
// except the requirements on the incoming parameter are different,
// ex: the memory op in storeind case must NOT be contained
void emitter::emitInsMov(instruction ins, emitAttr attr, GenTree* node)
{
    switch (node->OperGet())
    {
    case GT_IND:
        {
            GenTree* addr = node->gtGetOp1();
            assert(!addr->isContained());
            codeGen->genConsumeReg(addr);
            emitIns_R_R(ins, attr, node->gtRegNum, addr->gtRegNum);
        }
        break;

    case GT_STOREIND:
        {
            GenTree* addr = node->gtGetOp1();
            GenTree* data = node->gtOp.gtOp2;

            assert(!addr->isContained());
            assert(!data->isContained());
            codeGen->genConsumeReg(addr);
            codeGen->genConsumeReg(data);

            if (addr->OperGet() == GT_CLS_VAR_ADDR)
            {
                emitIns_C_R(ins, attr, addr->gtClsVar.gtClsVarHnd, data->gtRegNum, 0);
            }
            else
            {
                emitIns_R_R(ins, attr, addr->gtRegNum, data->gtRegNum);
            }
        }
        break;

    case GT_STORE_LCL_VAR:
        {
            GenTreeLclVarCommon* varNode = node->AsLclVarCommon();

            GenTree* data = node->gtOp.gtOp1->gtEffectiveVal();
            codeGen->inst_set_SV_var(varNode);
            assert(varNode->gtRegNum == REG_NA); // stack store

            if (data->isContainedIntOrIImmed())
            {
                emitIns_S_I(ins, attr, varNode->GetLclNum(), 0, (int) data->AsIntConCommon()->IconValue());
                codeGen->genUpdateLife(varNode);
            }
            else
            {
                assert(!data->isContained());
                codeGen->genConsumeReg(data);
                emitIns_S_R(ins, attr, data->gtRegNum, varNode->GetLclNum(), 0);
                codeGen->genUpdateLife(varNode);
            }
        }
        return;

    default:
        unreached();
    }
}

// The callee must call genConsumeReg() for any non-contained srcs
// and genProduceReg() for any non-contained dsts.

regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src)
{
    regNumber result = REG_NA;

    // dst can only be a reg
    assert(!dst->isContained());

    // src can be immed or reg
    assert(!src->isContained() || src->isContainedIntOrIImmed());

    // find immed (if any) - it cannot be a dst
    GenTreeIntConCommon* intConst = nullptr;
    if (src->isContainedIntOrIImmed())
    {
        intConst = src->AsIntConCommon();
    }

    if (intConst)
    {
        emitIns_R_I(ins, attr, dst->gtRegNum, intConst->IconValue());
        return dst->gtRegNum;
    }
    else
    {
        emitIns_R_R(ins, attr, dst->gtRegNum, src->gtRegNum);
        return dst->gtRegNum;
    }
}

#endif // !LEGACY_BACKEND
#endif // defined(_TARGET_ARM_)
